home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 001a / mskrmsrc.zip / MSSCMD.ASM < prev    next >
Assembly Source File  |  1991-10-24  |  75KB  |  2,363 lines

  1.     NAME    msscmd
  2. ; File MSSCMD.ASM
  3.     include mssdef.h
  4. ;       Copyright (C) 1982,1991, Trustees of Columbia University in the
  5. ;       City of New York.  Permission is granted to any individual or
  6. ;       institution to use, copy, or redistribute this software as long as
  7. ;       it is not sold for profit and this copyright notice is retained.
  8. ; Edit history:
  9. ; 6 Sept 1991 version 3.11
  10. ; 13 March 1991 version 3.10
  11. ; Last edit 13 March 1991
  12. ; 5 Jan 1991 Add \$(Environment variable) substitution.
  13. ; 9 Sept 1990 Add \v(named variable) substitution.
  14. ; 21 Nov 1988 Version 2.32
  15. ; 17 Oct 1988 Make command keyword search failure yield global kstatus of
  16. ;  failure status, to inform Take/Macros of a defective command.
  17. ; 4 Aug 1988 Fix keyword count initialization in help display.
  18. ; 1 July 1988 Version 2.31
  19. ; 7 June 1988 Add comand.impdo flag to permit a keyword search failure to
  20. ;  retry as a DO cmd (macro table). Used only by main Kermit command level.
  21. ; 27 May 1988 Allow "-<cr>" as line continuation pair and let "\-<cr>" 
  22. ;  stand for "-<end of line>". Add comand.cmblen to sense buffer overflow on
  23. ;  calls to cmtxt; if comand.cmblen is left at zero then use 128 byte limit
  24. ;  (comand.cmblen is cleared at command end).
  25. ; 15 May 1988 Make :label keywords no-ops, make query ordinary char in TAKEs.
  26. ; 6 May 1988 Show ambiguous keywords at parse time, do graceful interaction.
  27. ; 28 March 1988 Permit '\%x' (x = '0' or above) in commands as 'variables'
  28. ;  (substituted string of text). Words are obtained from Macro table mcctab.
  29. ;  DEF MAC and DO MAC define variables. DEF macro uses comand.cmper > 0 to
  30. ;  allow '\%x'to be stored as a literal. Variables work in any command except
  31. ;  DEF MAC. Major redesign of whole parser. [jrd]
  32. ; 4 March 1988 Rewrite keyword parsing to permit non-alphabetized tables
  33. ;  and 8-bit characters. Add byte comand.cmwhite which when non-zero permits
  34. ;  leading whitespace in cmline and cmword commands; it is reset at command
  35. ;  completion. Move procedure Prompt here from mssker. [jrd]
  36. ; 27 Feb 1988 Add capability of stdin being a file. [jrd]
  37. ; 1 Jan 1988 version 2.30
  38.  
  39.      public comnd, comand, isdev, iseof, prompt, tolowr, valbuf, valtab
  40.     public parstate, pardone, parfail, nparam, param, lparam, ninter
  41.     public inter, atparse, atpclr, atdispat, cspb, dspb, mprompt, nvaltoa
  42.     public keyboard
  43.  
  44. env    equ    2CH            ; environment address in psp
  45. braceop    equ    7bh            ; opening curly brace
  46. bracecl    equ    7dh            ; closing curly brace
  47.  
  48. data     segment
  49.     extrn    flags:byte, taklev:byte, takadr:word, mcctab:byte
  50.     extrn    kstatus:word, oldtak:byte, errlev:byte, psp:word
  51.     extrn    portval:word, bdtab:byte, tdfmt:byte, machnam:byte
  52.     extrn    verident:word, kstatus:word, errlev:byte, comptab:byte
  53.  
  54.                 ; Start Patch structure. Must be first.
  55.     even
  56. dspb    dw    code        ; segment values for patcher
  57.     dw    code1
  58.     dw    code2
  59.     dw    data
  60.     db    64 dup (0)    ; data segment patch buffer
  61.                 ;  with space for other material, if req'd
  62.                 ; end of Patch structure
  63. progm    db    'MS-DOS_KERMIT$'    ; for \v(program)
  64. system    db    'MS-DOS$'        ; for \v(system)
  65. keyboard dw    88            ; \v(keyboard) kind of keybd 88/101
  66.  
  67. comand    cmdinfo    <>
  68. cmer00  db      cr,lf,'?Program internal error, recovering$'
  69. cmer01    db    cr,lf,'?More parameters are needed$'
  70. cmer02    db    cr,lf,'?Word "$'
  71. cmer03    db    '" is not usable here$'
  72. cmer04    db    '" is ambiguous$'
  73. cmer07    db    cr,lf,'?Ignoring extra characters "$'
  74. cmer08    db    '"$'
  75. cmer09    db    cr,lf,'?Text exceeded available buffer capacity$'
  76. cmin00  db      ' Press ENTER to execute command$'
  77. cmin01    db    ' One of the following:',cr,lf,'$'
  78. stkmsg    db    cr,lf,bell,'?Exhausted work space! Circular definition?$'
  79. crlf    db      cr,lf,'$'
  80. ctcmsg    db    5eh,'C$'
  81. cmunk    db    'unknown'
  82. errflag    db    0            ; non-zero to suppress cmcfrm errors
  83. kwstat    db    0            ; get-keyword status
  84. prevch    db    0            ; previous char read by cmgetc
  85. noparse    db    0            ; semicolons not special, if non-zero
  86. subcnt    db    0            ; count of chars matched in '\%'
  87. subtype db    0            ; kind of sub (% or v)
  88. bracecnt db    0            ; curly brace counter
  89. cmsflg    db    0            ; Non-zero when last char was a space
  90. cmdbuf    db    cmdblen dup (0)        ; Buffer for command parsing
  91.     even
  92. cmdstk    dw    0            ; stack pointer at comand call time
  93. cmptab    dw    0            ; Address of present keyword table
  94. cmhlp    dw    0            ; Address of present help
  95. cmwptr    dw    0            ; Pointer for next char write
  96. cmrptr    dw    0            ; Pointer for next char read
  97. cmsiz    dw    0            ; Size info of user input
  98. cmsptr    dw    0            ; Place to save a pointer
  99. mcmprmp    dw    0            ; master prompt, string address
  100. mcmrprs    dd    0            ; master prompt, reparse address
  101. mcmostp    dw    0            ; master prompt, stack pointer
  102. temp    dw    0            ; temp (counts char/line so far)
  103.  
  104. valtab    db    16            ; table of values for \v(value)
  105.     mkeyw    'argc)',1
  106.     mkeyw    'count)',2
  107.     mkeyw    'date)',3
  108.     mkeyw    'ndate)',14
  109.     mkeyw    'directory)',5
  110.     mkeyw    'errorlevel)',4
  111.     mkeyw    'keyboard)',10
  112.     mkeyw    'line)',15
  113.     mkeyw    'platform)',8
  114.     mkeyw    'port)',15
  115.     mkeyw    'program)',12
  116.     mkeyw    'speed)',11
  117.     mkeyw    'status)',13
  118.     mkeyw    'system)',9
  119.     mkeyw    'time)',6
  120.     mkeyw    'version)',7
  121.  
  122. envtab    db    1                     ; \$(..) Environment table
  123.     mkeyw    'An Environment variable)',0    ; reserve 0 in valtab
  124.  
  125. valtmp    dw    0
  126. valbuf    db    130 dup (0)        ; storage of variable definition
  127.  
  128.     even
  129. envadr    dd    0            ; seg:offset of a string in Environemt
  130. envlen    dw    0            ; length of envadr's string
  131.  
  132.     even                ; Control sequence storage area
  133. maxparam equ    16            ; number of ESC and DCS Parameters
  134. maxinter equ    16            ; number of ESC and DCS Intermediates
  135. parstate dw    0            ; parser state, init to startup
  136. pardone dw    0            ; where to jmp after Final char seen
  137. parfail    dw    0            ; where to jmp if parser fails
  138. nparam    dw    0            ; number of received Parameters
  139. param    dw    maxparam dup (0)    ; Parameters for ESC
  140. lparam    db    0            ; a single letter Parameter for ESC
  141. ninter    dw    0            ; number of received Intermediates
  142. inter    db    maxinter dup (0)    ; Intermediates for ESC 
  143.  
  144. pcmcfrm    dw    offset cmcfrm,seg cmcfrm ; FAR pointers to main parser procs
  145. pcmkeyw    dw    offset cmkeyw,seg cmkeyw ; located in code segment code1
  146. pcmtext    dw    offset cmtext,seg cmtext
  147. pcmfil0    dw    offset cmfil0,seg cmfil0
  148. pprserr    dw    offset prserr,seg prserr
  149. pcmexit    dw    offset cm6,seg cm6
  150. pfvaltoa dw    offset fvaltoa,seg fvaltoa
  151. data    ends
  152.  
  153.  
  154. code    segment
  155.     extrn    ctlu:near, cmblnk:near, locate:near, takrd:near, dec2di:near
  156.     extrn    takclos:near, docom:near, prtasz:near, getenv:near
  157.     extrn    getbaud:near, strlen:near, prtscr:near
  158.  
  159.     assume    cs:code, ds:data, es:nothing
  160.  
  161.                 ; Patch area. Must be first in MSK's Code Seg
  162. cspb    db    256 dup (0)    ; code segment patch buffer
  163.                 ; end of Patch area
  164.  
  165. fctlu    proc    far        ; FAR callable versions of items in seg code
  166.     call    ctlu        ;  for calling from code segment code1 below
  167.     ret
  168. fctlu    endp
  169. fcmblnk    proc    far
  170.     call    cmblnk
  171.     ret
  172. fcmblnk    endp
  173. fgetbaud proc    far
  174.     call    getbaud
  175.     ret
  176. fgetbaud endp
  177. fgetenv    proc    far
  178.     call    getenv
  179.     ret
  180. fgetenv    endp
  181. flocate    proc    far
  182.     call    locate
  183.     ret
  184. flocate    endp
  185. ftakrd    proc    far
  186.     call    takrd
  187.     ret
  188. ftakrd    endp
  189. fdec2di    proc    far
  190.     call    dec2di
  191.     ret
  192. fdec2di    endp
  193. fstrlen    proc    far
  194.     call    strlen
  195.     ret
  196. fstrlen    endp
  197. ftakclos proc    far
  198.     call    takclos
  199.     ret
  200. ftakclos endp
  201. fprtasz    proc    far
  202.     call    prtasz
  203.     ret
  204. fprtasz    endp
  205. fprtscr    proc    far
  206.     call    prtscr
  207.     ret
  208. fprtscr    endp
  209. fisdev    proc    far
  210.     call    isdev
  211.     ret
  212. fisdev    endp
  213. fiseof    proc    far
  214.     call    iseof
  215.     ret
  216. fiseof    endp
  217. ftolowr    proc    far
  218.     call    tolowr
  219.     ret
  220. ftolowr    endp
  221. nvaltoa    proc    near
  222.     call    dword ptr pfvaltoa
  223.     ret
  224. nvaltoa    endp
  225.  
  226. ;       This routine parses the specified function in AH. Any additional
  227. ;       information is in DX and BX.
  228. ;       Returns carry clear on success and carry set on failure
  229.  
  230. COMND    PROC NEAR
  231.     mov    cmdstk,sp        ; save stack ptr for longjmp exit
  232.     mov    noparse,0          ; recognize semicolons in Take files
  233.     mov    bracecnt,0        ; curly brace counter
  234.     mov    al,taklev        ; Take level now
  235.     mov    oldtak,al        ; remember past internal takclos call
  236.     cmp    ah,cmeol        ; Parse a confirm?
  237.     jne    cm2            ; nz = no
  238.     call    dword ptr pcmcfrm    ; get a Carriage Return end of line
  239.     ret
  240. cm2:     cmp    ah,cmkey        ; Parse a keyword?
  241.     jne    cm3
  242.     call    dword ptr pcmkeyw    ; try and get one
  243.     ret
  244. cm3:    cmp    ah,cmline        ; parse line of text
  245.     jne    cm4
  246.     call    dword ptr pcmtext
  247.     ret
  248. cm4:    cmp    ah,cmword        ; parse arbitrary word
  249.     jne    cm5
  250.     call    dword ptr pcmfil0
  251.     ret
  252. cm5:    mov    ah,prstr        ; else give error
  253.     mov    dx,offset cmer00    ; "?Program internal error"
  254.     int    dos
  255.     jmp    dword ptr pprserr    ; reparse
  256.                     ; Control-C exit path (far to near)
  257. cm6:    mov    sp,cmdstk        ; restore command entry stack pointer
  258.     stc
  259.     ret                ;  and fail immediately (a longjmp)
  260.  
  261. COMND    ENDP
  262. code    ends
  263.  
  264. code1    segment
  265.     assume    cs:code1
  266.  
  267. ; This routine parses a keyword from the table pointed at by DX, help text
  268. ; point to by BX. Format of the table is as follows (use macro mkeyw):
  269. ;    addr:    db    N      ; Where N is the # of entries in the table
  270. ;        dw    M      ; M is the size of the keyword
  271. ;        db    'string'  ; String is the keyword
  272. ;        dw    value      ; Value is data to be returned
  273. ; Keywords may be in any order and in mixed case.
  274. ; Return is rskp for success and ret for failure.
  275.  
  276. ; cmptab: pointer to keyword table (supplied by caller)
  277. ; cmhlp: pointer to help message (supplied by caller)
  278. ; cmsptr: pointer to current user word text
  279. ; cmsiz: length of user text, excluding terminator
  280. ; comand.cmcr: 0 = empty lines not allowed, 1 = empty lines allowed
  281. ; comand.cmwhite: non-zero allows leading whitespace for cmline and cmword,
  282. ;                 reset automatically at end of call
  283. ; cmwptr: buffer write pointer to next free byte
  284. ; cmrptr: buffer read pointer for next free byte
  285. ; comand.cmper:    0 to do \%x substitution. Set to 0 at end of call
  286. ; comand.impdo: non-zero permits keyword failure to retry as DO command, reset
  287. ;         automatically at time of failure.
  288. cmkeyw    proc    far
  289.     mov    cmsiz,0            ; user word length
  290.     mov    ax,cmrptr        ; get command reading pointer
  291.     mov    cmsptr,ax        ; set pointer for start of user word
  292.     mov    cmhlp,bx        ; save the help pointer
  293.         mov    cmptab,dx        ; save the beginning of keyword table
  294.     mov    bx,dx
  295.     cmp    byte ptr[bx],0        ; get number of entries in table
  296.     jne    cmky1
  297.     jmp    cmky7            ; e = no keywords to check, error
  298. cmky1:    mov    cmsflg,0ffh        ; skip leading spaces/tabs
  299.     call    cmgtch            ; get char from the user into ah
  300.     jc    cmky3            ; c = terminator
  301.     mov    dx,cmrptr        ; next byte to read
  302.     dec    dx            ; where we just read a char
  303.     mov    cmsptr,dx        ; remember start of keyword
  304.     inc    cmsiz            ; start counting user chars
  305. cmky2:    call    cmgtch            ; read until terminator
  306.     jc    cmky3            ; c = terminator
  307.     inc    cmsiz            ; count user chars
  308.     jmp    short cmky2        ; no terminator yet
  309.  
  310. cmky3:    cmp    ah,'?'                  ; need help?
  311.     jne    cmky4            ; ne = no
  312.     jmp    cmkyhlp            ; do help and exit
  313. cmky4:    cmp    ah,escape        ; escape?
  314.     jne    cmky6            ; ne = no
  315.     call    cmkyesc            ; process escape
  316.     jc    cmky5            ; c = failure (no unique keyword yet)
  317.     mov    comand.cmper,0        ; reset to variable recognition
  318.     mov    comand.cmkeep,0
  319.     mov    comand.impdo,0        ; clear flag to prevent loops
  320.     mov    comand.cmquiet,0    ; permit echoing again
  321.     clc
  322.     ret                ; return successfully to user
  323.  
  324. cmky5:    cmp    cmsiz,0            ; started a word yet?
  325.     je    cmky1            ; e = no, ignore escape, keep looking
  326.     jmp    cmkyhlp            ; ne = yes, show type of error
  327.  
  328. cmky6:    cmp    cmsiz,0            ; length of user's text, empty?
  329.     je    cmky7            ; e = yes, parse error
  330.     push    bx
  331.     mov    bx,cmsptr        ; point at first user character
  332.     cmp    byte ptr[bx],':'    ; start of a label?
  333.     pop    bx
  334.     jne    cmky6a            ; ne = no, return success
  335.     mov    cmsiz,1            ; say just one byte
  336. cmky6a:    call    getkw            ; get unique kw, point to it with bx
  337.     jc    cmky8            ; c = not found
  338.     add    bx,[bx]            ; add length of keyword text (CNT)
  339.     add    bx,2            ; point at value field
  340.     mov    bx,[bx]            ; bx = return value following keyword
  341.     xor    ax,ax
  342.     mov    comand.cmper,al        ; reset to variable recognition
  343.     mov    comand.cmkeep,al
  344.     mov    comand.impdo,al        ; clear flag to prevent loops
  345.     mov    comand.cmquiet,al    ; permit echoing again
  346.     mov    errflag,al
  347.     clc
  348.     ret                ; return successfully
  349.                     ; all other terminators come here
  350. cmky7:    cmp    cmsiz,0            ; empty table or empty user's text?
  351.     jne    cmky8            ; ne = no
  352.     cmp    comand.cmcr,0        ; empty lines allowed?
  353.     jne    cmky10            ; ne = yes, do not complain
  354.     push    dx
  355.     mov    ah,prstr
  356.     mov    dx,offset cmer01    ; command word expected
  357.     int    dos
  358.     pop    dx
  359.     xor    al,al
  360.     mov    comand.cmquiet,al    ; permit echoing again
  361.     mov    comand.impdo,al        ; clear flag to prevent loops
  362.     stc                ; failure
  363.     ret
  364.  
  365. cmky8:    cmp    comand.impdo,0        ; failed here, ok to try Macro table?
  366.     je    cmky8a            ; e = no, use regular exit path
  367.     mov    comand.impdo,0        ; yes, but clear flag to prevent loops
  368.     mov    cmrptr,offset cmdbuf    ; reinit read pointer
  369.     mov    comand.cmquiet,1    ; suppress echoing of same keyword
  370.     mov    bx,offset docom        ; return DO as "found" keyword
  371.     clc
  372.     ret                ; return success to invoke DO
  373.  
  374. cmky8a:    mov    errflag,1        ; say already doing error recovery
  375.     or    kstatus,ksgen        ; global command status, failure
  376.     mov    comand.cmquiet,0    ; permit echoing again
  377.     call    fisdev            ; reading pretyped lines?
  378.     jnc    cmky9            ; nc = yes, consume rest of line
  379.     cmp    taklev,0        ; in a Take file?
  380.     jne    cmky9            ; ne = yes
  381.     call    cmskw            ; display offending keyword
  382.     dec    cmrptr            ; interactive, backup to terminator
  383.     mov    bx,cmrptr        ; look at it
  384.     cmp    byte ptr [bx],' '    ; got here on space terminator?
  385.     jne    cmky10            ; ne = no, (cr,lf,ff) exit failure
  386.     mov    ah,prstr        ; start a fresh line
  387.     mov    dx,offset crlf
  388.     int    dos
  389.     call    bufreset        ; cut back buffer to just before term
  390.     jmp    repars            ; reparse interactive lines
  391.  
  392. cmky9:    call    dword ptr pcmcfrm    ; get formal end of command line
  393.                     ;  to maintain illusion of typeahead
  394.                     ;  and let user backspace to correct
  395.                     ;  mistakes (we reparse everything)
  396.     call    cmskw            ; display offending keyword
  397. cmky10:    mov    comand.cmquiet,0    ; permit echoing again
  398.     stc                ; say failure
  399.     ret
  400. cmkeyw    endp
  401.  
  402. ;;;;;; start support routines for keyword parsing.
  403.  
  404. cmkyesc    proc    near            ; deal with escape terminator
  405.     call    bufreset        ; reset buffer to end just before ESC
  406.     cmp    cmsiz,0         ; user word length, empty?
  407.     jne    cmkye2            ; ne = have user text, else complain
  408. cmkye1:    call    esceoc            ; do normal escape end-of-command
  409.     stc                ; say failure to fill out word
  410.     ret
  411.                     ; add unique keyword to buffer
  412. cmkye2:    call    getkw            ; is there a matching keyword?
  413.     jc    cmkye1            ; c = ambiguous or not found
  414.     push    bx            ; unique, bx points to structure
  415.     push    si
  416.     mov    cx,[bx]            ; length of keyword
  417.     add    bx,2            ; point to first letter
  418.     mov    si,cmwptr        ; where next char goes
  419.     mov    dx,cmsiz        ; length of user word
  420.     add    bx,dx            ; add chars known so far
  421.     sub    cx,dx            ; calculate number yet to add
  422.     add    cmsiz,cx
  423.     jcxz    cmkye4            ; z = none
  424.     mov    ah,conout        ; display new char
  425. cmkye3:    mov    al,[bx]            ; get a keyword letter
  426.     inc    bx
  427.     call    ftolowr            ; lowercase
  428.     mov    [si],al            ; store it
  429.     inc    si
  430.     mov    dl,al
  431.     int    dos            ; display it
  432.     loop    cmkye3            ; do all new chars
  433. cmkye4:    mov    byte ptr[si],' '    ; insert space terminator in buffer
  434.     mov    dl,' '            ; display it
  435.     mov    ah,conout
  436.     int    dos
  437.     inc    si
  438.     mov    cmrptr,si        ; move token pointer after the space
  439.     mov    cmwptr,si        ; next free slot is after the space
  440.     mov    cmsflg,0ffh        ; set space-seen flag
  441.     pop    si
  442.     pop    bx            ; bx = keyword structure
  443.     add    bx,[bx]            ; add length of keyword text
  444.     add    bx,2            ; point at value field
  445.     mov    bx,[bx]            ; bx = return value following keyword
  446.     clc                ; carry clear for success
  447.     ret
  448. cmkyesc    endp
  449.  
  450. esceoc    proc    near            ; do normal escape end-of-command
  451.     push    ax
  452.     push    dx
  453.     mov    ah,conout        ; ring the bell
  454.     mov    dl,bell
  455.     int    dos
  456.     pop    dx
  457.     pop    ax
  458.     call    bufreset        ; reset buffer
  459.     stc                ; say error condition
  460.     ret
  461. esceoc    endp
  462.  
  463. ; Help. Question mark entered by user.  Display all the keywords that match
  464. ; user text. If text is null then use external help if available; otherwise,
  465. ; display all keywords in the table. Removes question mark from buffer and
  466. ; invokes reparse of command line to-date. User word starts at cmsptr and
  467. ; is cmsiz bytes long.
  468. cmkyhlp    proc    near
  469.     xor    cx,cx            ; clear number of keyword (none yet)
  470.     cmp    cmsiz,0            ; user text given?
  471.     jne    cmkyh1            ; ne = yes, use matching keywords
  472.     cmp    cmhlp,0            ; external help given?
  473.     jne    cmkyh6            ; yes, use it instead of full table
  474. cmkyh1:    mov    temp,0            ; count # chars printed on this line
  475.     mov    bx,cmptab        ; beginning of kw table
  476.     mov    ch,[bx]            ; length of table
  477.     xor    cl,cl            ; no keywords or help displayed yet
  478.     inc    bx            ; point at CNT field
  479. cmkyh2:    cmp    cmsiz,0            ; length of user word
  480.     je    cmkyh3            ; e = null, use full table
  481.     call    cmpwrd            ; compare keyword with user word
  482.     jc    cmkyh5            ; c = no match, get another keyword
  483. cmkyh3:    mov    ax,[bx]            ; length of table keyword
  484.     add    byte ptr temp,al    ; count chars printed so far
  485.     cmp    temp,76            ; will this take us beyond column 78?
  486.     jbe    cmkyh4            ; be = no, line has more room
  487.     mov    byte ptr temp,al    ; reset the count
  488.     mov    ah,prstr
  489.     mov    dx,offset crlf        ; break the line
  490.     int    dos
  491. cmkyh4:    or    cl,cl            ; any keywords found yet?
  492.     jnz    cmkyh4a            ; nz = yes
  493.     mov    dx,offset cmin01    ; start with One of the following: msg
  494.     mov    ah,prstr
  495.     int    dos
  496.     inc    cl            ; say one keyword has been found
  497. cmkyh4a:mov    dl,spc            ; put two spaces before each keyword
  498.     mov    ah,conout
  499.     int    dos
  500.     int    dos
  501.     add    temp,2            ; count output chars
  502.     mov    di,bx            ; get current keyword structure
  503.     add    di,2            ; text part
  504.     push    cx
  505.     mov    cx,[bx]            ; string length to cx, offset to di
  506.     call    fprtscr            ; display counted string
  507.     pop    cx
  508. cmkyh5:    dec    ch            ; are we at end of table?
  509.     jle    cmkyh7            ; le = yes, quit now
  510.     add    bx,[bx]            ; next keyword, add CNT chars to bx
  511.     add    bx,4            ; skip CNT and 16 bit value
  512.     jmp    cmkyh2            ; go examine this keyword
  513.  
  514. cmkyh6:    mov    dx,cmhlp        ; external help text
  515.     mov    ah,prstr
  516.     int    dos
  517.     inc    cl            ; say gave help already
  518. cmkyh7:    or    cl,cl            ; found any keywords?
  519.     jnz    cmkyh9            ; nz = yes
  520.     mov    cx,cmsiz        ; length of word
  521.     or    cx,cx
  522.     jg    cmkyh8            ; g = something to show
  523.     push    dx
  524.     mov    ah,prstr
  525.     mov    dx,offset cmer01    ; command word expected
  526.     int    dos
  527.     pop    dx
  528.     jmp    prserr
  529. cmkyh8:    mov    kwstat,0        ; set keyword not-found status
  530.     call    cmskw            ; display offending keyword
  531. cmkyh9:    mov    ah,prstr        ; start a fresh line
  532.     mov    dx,offset crlf
  533.     int    dos
  534.     call    bufreset        ; cut back buffer to just before '?'
  535.         jmp    repars
  536. cmkyhlp    endp
  537.  
  538. ; See if keyword is ambiguous or not from what the user has typed in.
  539. ; Return carry set if word is ambiguous or not found, carry clear otherwise.
  540. ; Uses table pointed at by cmptab, user text pointed at by cmsptr and length
  541. ; in cmsiz.
  542. cmambg    proc    near
  543.     push    bx
  544.     push    cx
  545.     push    dx
  546.     xor    dl,dl            ; count keyword matches so far
  547.     mov    bx,cmptab        ; look at start of keyword table
  548.     mov    cl,[bx]            ; get number of entries in table
  549.     xor    ch,ch            ; use cx as a counter
  550.     jcxz    cmamb8            ; z = no table so always ambiguous
  551.     inc    bx            ; look at CNT byte of keyword
  552. cmamb4:    call    cmpwrd            ; user vs table words, same?
  553.     jc    cmamb6            ; c = no match
  554.     inc    dl            ; count this as a match
  555.     cmp    dl,1            ; more than one match?
  556.     ja    cmamb8            ; a = yes, quit now
  557. cmamb6:    add    bx,[bx]            ; add CNT chars to bx
  558.     add    bx,4            ; skip CNT and 16 bit value
  559.     loop    cmamb4            ; do rest of keyword table
  560.     cmp    dl,1            ; how many matches were found?
  561.     jne    cmamb8            ; ne = none or more than 1: ambiguous
  562.     pop    dx            ; restore main registers
  563.     pop    cx
  564.     pop    bx
  565.     clc
  566.     ret                ; ret = not ambiguous
  567. cmamb8:    pop    dx            ; restore main registers
  568.     pop    cx
  569.     pop    bx
  570.     stc
  571.     ret                ; return ambiguous or not found
  572. cmambg    endp
  573.  
  574. ; Compare user text with keyword, abbreviations are considered a match.
  575. ; Enter with bx pointing at keyword table CNT field for a keyword.
  576. ; Return carry clear if they match, set if they do not match. User text
  577. ; pointed at by cmsptr and length is in cmsiz.
  578. ; Registers preserved.
  579.  
  580. cmpwrd    proc    near
  581.     push    cx
  582.     mov    cx,cmsiz        ; length of user's text
  583.     jcxz    cmpwrd2            ; z: null user word matches no keyword
  584.     cmp    cx,[bx]            ; user's text longer than keyword?
  585.     ja    cmpwrd2            ; a = yes, no match
  586.     push    ax
  587.     push    bx
  588.     push    si
  589.     add    bx,2                ; point at table's keyword text
  590.     mov    si,cmsptr        ; buffer ptr to user input
  591.     cld
  592. cmpwrd1:lodsb                ; user text
  593.     mov    ah,[bx]            ; keyword text
  594.     inc    bx            ; next keyword letter
  595.     call    ftolowr            ; force lower case on both chars
  596.     cmp    ah,al            ; same?
  597.     loope    cmpwrd1            ; e = same so far
  598.     pop    si
  599.     pop    bx
  600.     pop    ax
  601.     jne    cmpwrd2            ; ne = mismatch
  602.     pop    cx            ; recover keyword counter
  603.     clc                ; they match
  604.     ret
  605. cmpwrd2:pop    cx            ; recover keyword counter
  606.     stc                ; they do not match
  607.     ret
  608. cmpwrd    endp
  609.  
  610. ; Get pointer to keyword structure using user text. Uses keyword table
  611. ; pointed at by cmptab and cmsiz holding length of user's keyword (cmpwrd
  612. ; needs comand.cmsptr pointing at user's keyword and length of cmsiz).
  613. ; Structure pointer returned in BX.
  614. ; Return carry clear for success and carry set for failure. Modifies BX.
  615. getkw    proc    near
  616.     push    cx
  617.     mov    kwstat,0        ; keyword status, set to not-found
  618.     cmp    cmsiz,0            ; length of user word, empty?
  619.     je    getkw3            ; e = yes, fail
  620.     mov    bx,cmptab        ; table of keywords
  621.     mov    cl,[bx]            ; number of keywords in table
  622.     xor    ch,ch
  623.     jcxz    getkw3            ; z = none, fail
  624.     inc    bx            ; point to first
  625. getkw1:    call    cmpwrd            ; compare user vs table words
  626.     jc    getkw2            ; c = failed to match word, try next
  627.     mov    kwstat,1        ; say found one keyword, maybe more
  628.     push    dx
  629.     mov    dx,cmsiz        ; users word length
  630.     cmp    [bx],dx            ; same length (end of keyword)?
  631.     pop    dx
  632.     je    getkw4            ; e = yes, exact match. Done
  633.     call    cmambg            ; ambiguous?
  634.     jnc    getkw4            ; nc = unique, done, return with bx
  635.     mov    kwstat,2        ; say more than one such keyword
  636. getkw2:    add    bx,[bx]            ; next keyword, add CNT chars to bx
  637.     add    bx,4            ; skip CNT and 16 bit value
  638.     loop    getkw1            ; do all, exhaustion = failure
  639. getkw3:    pop    cx
  640.     stc                ; return failure
  641.     ret
  642. getkw4:    pop    cx
  643.     clc                ; return success
  644.     ret
  645. getkw    endp
  646.  
  647. ; show offending keyword message. Cmsptr points to user word,
  648. ; cmsiz has length. Modifies AX, CX, and DX.
  649. cmskw    proc    near
  650.     cmp    comand.cmquiet,0    ; Quiet mode?
  651.     je    cmskw0            ; e = no, regular mode
  652.     ret                ; else say nothing
  653. cmskw0:    mov    ah,prstr        ; not one of the above terminators
  654.     mov    dx,offset cmer02    ; '?Word "'
  655.     int    dos
  656.     mov    ah,conout
  657.     mov    cx,cmsiz        ; length of word
  658.     jcxz    cmskw3            ; z = null
  659.     mov    ah,conout
  660.     push    si
  661.     mov    si,cmsptr        ; point to word
  662.     cld
  663. cmskw1:    lodsb
  664.     cmp    al,' '            ; control code?
  665.     jae    cmskw2            ; ae = no
  666.     push    ax
  667.     mov    dl,5eh            ; caret
  668.     int    dos
  669.     pop    ax
  670.     add    al,'A'-1        ; plus ascii bias
  671. cmskw2:    mov    dl,al            ; display chars in word
  672.     int    dos
  673.     loop    cmskw1
  674.     pop    si
  675. cmskw3:    mov    dx,offset cmer03    ; '" not usable here.'
  676.     cmp    kwstat,1        ; kywd status from getkw, not found?
  677.     jb    cmskw4            ; b = not found, a = ambiguous
  678.     mov    dx,offset cmer04    ; '" ambiguous'
  679. cmskw4:    mov    ah,prstr
  680.         int    dos
  681.     ret
  682. cmskw    endp
  683. ;;;;;;;;;; end of support routines for keyword parsing.
  684.  
  685. ; Parse    arbitrary text up to a CR. Enter with BX = pointer to output buffer,
  686. ; DX pointing to help text. Produces asciiz string. Return updated pointer in
  687. ; BX and output size in AH. Leading spaces are omitted unless comand.cmwhite
  688. ; is non-zero (cleared upon exit). It does not need to be followed by the
  689. ; usual call to confirm the line. Byte comand.cmblen can be used to specify
  690. ; the length of the caller's buffer; cleared to zero by this command to
  691. ; imply a length of 127 bytes (default) and if zero at startup use 127 bytes.
  692. cmtext    proc    far
  693.     mov    cmptab,bx        ; save pointer to data buffer
  694.     mov    cmhlp,dx        ; save the help message
  695.     xor    cx,cx            ; init the char count
  696.     cmp    comand.cmblen,0        ; length of user's buffer given?
  697.     jne    cmtxt0            ; ne = yes
  698.     mov    comand.cmblen,127    ; else set 127 byte limit plus null
  699. cmtxt0:    mov    cmsflg,0ffh        ; skip initial spaces
  700.     cmp    comand.cmwhite,0    ; allow leading whitespace?
  701.     je    cmtxt1a            ; e = no
  702. cmtxt1:    mov    cmsflg,0        ; get all spaces
  703. cmtxt1a:call    cmgtch            ; get a char
  704.     jnc    cmtxt5            ; nc = non-terminator, put in buffer
  705.     cmp    ah,' '            ; space?
  706.     je    cmtxt5            ; e = yes, record it
  707.     cmp    ah,escape        ; escape?
  708.     jne    cmtxt2            ; ne = no
  709.     call    esceoc            ; do normal escape end-of-command
  710.     jmp    short cmtxt0        ; try again
  711.  
  712. cmtxt2:    cmp    ah,'?'            ; asking a question?
  713.     je    cmtxt3            ; e = yes
  714.     cmp    ah,cr            ; formal carriage return?
  715.     je    cmtxt2a            ; e = yes
  716.     inc    cmrptr            ; accept char into buffer
  717.     jmp    short cmtxt5        ;  and record the char
  718. cmtxt2a:mov    bx,cmptab        ; return updated pointer
  719.     xor    ax,ax
  720.     mov    byte ptr[bx],al        ; put terminator into the buffer
  721.     mov    comand.cmwhite,al    ; clear leading whitespace flag
  722.     mov    comand.cmper,al        ; reset to variable recognition
  723.     mov    comand.cmblen,ax    ; set user buffer length to unknown
  724.     mov    comand.cmkeep,al
  725.     mov    ax,cx            ; return count in AX
  726.     call    rprompt            ; restore master prompt level
  727.     clc
  728.     ret
  729.                     ; Help processor
  730. cmtxt3:    inc    cmrptr            ; count the ?
  731.     or    cx,cx            ; is "?" the first char?
  732.     jnz    cmtxt5            ; nz = no, just add to buffer
  733.     dec    cmrptr
  734.     mov    cmsiz,0            ; no keyword for help
  735.     mov    comand.cmwhite,0    ; clear leading whitespace flag
  736.     cmp    cmhlp,0            ; external help given?
  737.     jne    cmtxt3a            ; ne = yes
  738.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  739.     mov    comand.cmblen,0        ; set user buf length to unknown
  740. cmtxt3a:jmp    cmkyhlp            ; do help process
  741.  
  742. cmtxt5:    inc    cx            ; increment the count
  743.     mov    bx,cmptab        ; pointer into destination array
  744.     mov    [bx],ah            ; put char into the buffer
  745.     inc    bx
  746.     xor    al,al
  747.     mov    [bx],al            ; insert null terminator
  748.     mov    cmptab,bx
  749.     cmp    cx,comand.cmblen    ; buffer filled?
  750.     ja    cmtxt6            ; a = yes, declare error
  751.     jb    cmtxt5a            ; a = not filled yet
  752.     mov    ah,conout        ; notify user that the buffer is full
  753.     mov    dl,bell
  754.     int    dos
  755. cmtxt5a:jmp    cmtxt1
  756. cmtxt6:    mov    ah,prstr
  757.     mov    dx,offset cmer09
  758.     int    dos
  759.     jmp    prserr            ; declare parse error
  760. cmtext    endp
  761.  
  762.  
  763. ; Parse arbitrary text up to whitespace.  Enter with DX pointing to output
  764. ; buffer and BX pointing to help text. Produces asciiz string. Return updated
  765. ; pointer in DX and input size in AH. Skips leading whitespace unless 
  766. ; comand.cmwhite is non-zero (cleared upon exit). Does a return skip exit.
  767.  
  768. cmfil0    proc    far
  769.     mov    cmptab,dx        ; save pointer to data buffer
  770.     mov    cmhlp,bx        ; save the help message
  771.     mov    cmsiz,0            ; init the char count
  772.     cmp    comand.cmblen,0        ; length of user's buffer given?
  773.     jne    cmfil0a            ; ne = yes
  774.     mov    comand.cmblen,127    ; else set 127 byte limit plus null
  775. cmfil0a:cmp    comand.cmwhite,0    ; allow leading whitespace?
  776.     jne    cmfil1            ; ne = yes
  777.     mov    cmsflg,0ffh        ; omit leading space
  778. cmfil1:    call    cmgtch            ; get a char
  779.     jc    cmfi1a            ; c = terminator 
  780.     jmp    short cmfil5        ; put char into the buffer
  781. cmfi1a:    cmp    ah,escape        ; escape?
  782.     je    cmfi1b            ; e = yes
  783.     jmp    short cmfil2        ; process other terminators
  784. cmfi1b:    call    esceoc            ; do normal escape end-of-command
  785.     jmp    short cmfil0a        ; try again
  786.  
  787. cmfil2:    cmp    ah,'?'            ; asking a question?
  788.     je    cmfil3            ; e = yes
  789.     xchg    dx,bx            ; re-interchange bx and dx
  790.     xor    ax,ax
  791.     mov    comand.cmwhite,al    ; clear whitespace flag
  792.     mov    comand.cmper,al        ; reset to variable recognition
  793.     mov    comand.cmkeep,al    ; do not keep Take file open
  794.     mov    comand.cmblen,ax    ; set user buffer length to unknown
  795.     mov    bx,cmptab        ; pointer into destination array
  796.     mov    byte ptr[bx],al        ; put null terminator into the buffer
  797.     inc    bx
  798.     mov    ax,cmsiz        ; return count in AX
  799.     mov    dx,cmptab        ; return updated pointer
  800.     xchg    dx,bx            ; re-interchange bx and dx
  801.     call    rprompt            ; restore master prompt level
  802.     clc
  803.     ret                ; return success
  804.  
  805. cmfil3:    inc    cmrptr            ; count the ?
  806.     cmp    cmsiz,0            ; Is "?" first char?
  807.     jne    cmfil5            ; ne = no, just add to buffer
  808.     dec    cmrptr
  809.     mov    cmsiz,0
  810.     cmp    cmhlp,0            ; external help given?
  811.     jne    cmfil3a            ; ne = yes
  812.     mov    cmhlp,offset cmin00    ; confirm with c/r msg
  813. cmfil3a:jmp    cmkyhlp            ; do help process
  814.  
  815. cmfil5:    inc    cmsiz            ; increment the count
  816.     mov    bx,cmptab        ; pointer into destination array
  817.     mov    [bx],ah            ; put char into the buffer
  818.     inc    bx
  819.     mov    cmptab,bx
  820.     mov    cx,cmsiz        ; length of command so far
  821.     cmp    cx,comand.cmblen    ; buffer filled?
  822.     ja    cmfil7            ; a = yes, declare error
  823.     jb    cmfil6            ; a = not filled yet
  824.     mov    ah,conout        ; notify user that the buffer is full
  825.     mov    dl,bell
  826.     int    dos
  827. cmfil6:    jmp    cmfil1
  828.  
  829. cmfil7:    mov    ah,prstr
  830.     mov    dx,offset cmer09
  831.     int    dos
  832.     jmp    prserr            ; declare parse error
  833. cmfil0    endp
  834.  
  835. ; This routine gets a confirm (CR) and displays any extra non-blank text.
  836. ; errflag non-zero means suppress "extra text" display in this routine
  837. ; because another routine is handling errors.
  838. cmcfrm    proc    far
  839.     mov    comand.cmper,1        ; do not react to \%x substitutions
  840. cmcfr1:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  841.     call    cmgtch            ; get a char
  842.     push    cmrptr
  843.     pop    temp            ; remember first non-space position
  844.     jc    cmcfr4            ; c = terminator
  845.     dec    temp            ; backup to text char
  846. cmcfr3:    mov    cmsflg,0ffh        ; set space-seen flag (skip spaces)
  847.     call    cmgtch
  848.     jnc    cmcfr3            ; read until terminator
  849. cmcfr4:    cmp    ah,' '
  850.     je    cmcfr3            ; ignore ending on space
  851.     cmp    ah,escape        ; escape?
  852.     jne    cmcfr5            ; ne = no
  853.     call    esceoc            ; do standard end of cmd on escape
  854.     mov    ax,cmrptr
  855.     cmp    ax,temp            ; started text yet?
  856.     je    cmcfr1            ; e = no
  857.     jmp    short cmcfr3        ; try again
  858. cmcfr5: cmp    ah,'?'            ; curious?
  859.         jne    cmcfr6            ; ne = no
  860.     mov    cmhlp,offset cmin00    ; msg Confirm with c/r
  861.     mov    cmsiz,0            ; no keyword
  862.     mov    errflag,0
  863.     jmp    cmkyhlp            ; do help
  864. cmcfr6:    cmp    ah,cr            ; the confirmation char?
  865.     jne    cmcfr3            ; ne = no
  866.     cmp    errflag,0        ; already doing one error?
  867.     jne    cmcfr7            ; ne = yes, skip this one
  868.     mov    cx,cmrptr        ; pointer to terminator
  869.     mov    dx,temp            ; starting place
  870.     sub    cx,dx            ; end minus starting point = length
  871.     jle    cmcfr7            ; le = nothing to display
  872.     push    dx            ; save source pointer
  873.     mov    ah,prstr
  874.     mov    dx,offset cmer07    ; ?Ignoring extras
  875.     int    dos
  876.     pop    dx
  877.     mov    bx,1            ; stdout handle, cx=count, dx=src ptr
  878.     mov    ah,write2        ; allow embedded dollar signs
  879.     int    dos
  880.     mov    ah,prstr
  881.     mov    dx,offset cmer08    ; trailer msg
  882.     int    dos
  883. cmcfr7:    mov    errflag,0
  884.     mov    comand.cmper,0        ; reset to variable recognition
  885.     mov    comand.cmkeep,0
  886.     call    rprompt            ; restore master prompt level
  887.     clc                ; return confirmed
  888.     ret
  889. cmcfrm    endp
  890.  
  891. ;;; Routines to get and edit incoming text.
  892.  
  893. ; Detect '\%x' (x = '0' or above) and substitute the matching Macro string
  894. ; in place of the '\%x' phrase in the user's buffer. If comand.cmper != 0
  895. ; then treat '\%' as literal characters. If no matching parameter exists
  896. ; just remove '\%x'. Ditto for \v(variable). Returns carry clear if nothing
  897. ; done, else carry set and new text already placed in user's buffer.
  898. ; Includes \v(variable) and \$(Environment variable) and \m(macro name).
  899. ; Uses depth-first recursion algorithm. All registers preserved.
  900. subst    proc    near
  901.     cmp    comand.cmper,0        ; recognize '\%','\v(','\$(','\m(' ?
  902.     jne    subst2            ; ne = no, treat as literals
  903.     cmp    ah,'\'            ; is it the first char of the pattern?
  904.     jne    subst1            ; ne = no, try next
  905.     mov    subcnt,1        ; say first is matched
  906.     mov    subtype,0
  907.     jmp    short subst2        ; exit successfully
  908. subst1:    cmp    subcnt,1        ; first char matched already?
  909.     ja    subst3            ; a = first two have been matched
  910.     jb    subst2            ; b = none yet
  911.     inc    subcnt            ; assume a match follows
  912.     mov    subtype,ah        ; remember kind of substitution
  913.     or    subtype,20h        ; convert to lower case
  914.     cmp    ah,'%'            ; second match char, same?
  915.     je    subst2            ; e = yes
  916.     cmp    subtype,'v'        ; \v(...)?
  917.     je    subst2            ; e = yes
  918.     cmp    subtype,'$'        ; \$(..)?
  919.     je    subst2
  920.     cmp    subtype,'m'        ; \m(..)?
  921.     je    subst2
  922. subst1a:mov    subcnt,0        ; mismatch, clear match counter
  923.     mov    subtype,0        ; clear substitution kind
  924. subst2:    clc                ; carry clear = no substitution done
  925.     ret
  926. subst3:    cmp    subtype,'v'        ; doing \v(..)?
  927.     je    subst3a            ; e = yes
  928.     cmp    subtype,'$'        ; doing \$(..)?
  929.     je    subst3a            ; e = yes
  930.     cmp    subtype,'m'        ; doing \m(..)?
  931.     jne    subst3b            ; ne = no, do \%<char>
  932. subst3a:cmp    ah,'('            ; have leading parenthesis?
  933.     jne    subst1a            ; ne = no, mismatch, exit
  934.     jmp    subst10            ; process \v(..), \$(..), \m(..)
  935. subst3b:mov    subcnt,0        ; do \%<char>, clear match counter
  936.     cmp    ah,'0'            ; third char is '0' or above?
  937.     jb    subst1a            ; b = out of range, no match
  938.     push    bx            ; save working regs
  939.     push    cx
  940.     push    es
  941.     sub    cmrptr,3        ; reread commands where backslash was
  942.     call    bufreset        ; reset buffer to this point
  943.     push    cmptab            ; save current keyword parsing parms
  944.     push    cmsptr
  945.     push    cmsiz
  946.     mov    bx,cmrptr        ; points at backslash
  947.     mov    cmsptr,bx        ; direct keyword routine to it
  948.     mov    cmsiz,3            ; three bytes (\%x) of user text
  949.     mov    cmptab,offset mcctab     ; use Macro table for new text
  950.     call    getkw            ; get ptr, bx, to matching keyword
  951.     pop    cmsiz            ; restore borrowed keyword parameters
  952.     pop    cmsptr
  953.     pop    cmptab
  954.     jc    substx            ; c = not found, keep after pops
  955.     cmp    taklev,maxtak        ; room in take level?
  956.     jae    subst6            ; ae = no
  957.     mov    cx,[bx]            ; length of found word
  958.     add    cx,2            ; plus count field
  959.     add    bx,cx            ; point to 16 bit value (string ptr)
  960.     mov    es,[bx]            ; point to string structure segment
  961.     xor    bx,bx
  962.     mov    cx,es:[bx]        ; length of string
  963.     add    takadr,size takinfo    ; pointer to new Take structure
  964.     inc    taklev
  965.     mov    bx,takadr        ; pointer to new Take structure
  966.     mov    [bx].takbuf,es        ; segment of Take buffer
  967.     mov    [bx].takcnt,cx        ; number of unread bytes
  968.     mov    [bx].taktyp,0fdh    ; flag as an internal macro
  969.     mov    [bx].takptr,2        ; init pointer to definition itself
  970. substx:    pop    es
  971.     pop    cx
  972.     pop    bx
  973.     stc                ; set carry to say have stored chars
  974.     ret
  975.  
  976. subst6:    mov    ah,prstr
  977.     mov    dx,offset stkmsg    ; out of work space msg
  978.     int    dos
  979.     jmp    prserr            ; and declare parse error
  980.  
  981.                     ; \m(..), \v(..), \$(..)
  982. subst10:push    bx            ; save working regs
  983.     push    cx
  984.     push    es
  985.     push    cmptab            ; save current keyword parsing parms
  986.     push    cmsptr
  987.     push    cmsiz
  988.     push    cmhlp
  989.     push    dx
  990.     mov    subcnt,0        ; clear match counter
  991.     mov    cmhlp,0
  992.     mov    ax,cmrptr
  993.     mov    valtmp,ax        ; remember current read pointer
  994.     mov    cmsptr,ax        ; start of word
  995.     mov    cmptab,offset valtab    ; table of keywords for \v(..)
  996.     cmp    subtype,'v'        ; \v(..)?
  997.     je    subst10a        ; e = yes
  998.     mov    cmptab,offset envtab    ; Environment variable table \$(..)
  999.     cmp    subtype,'$'        ; \$(..)?
  1000.     je    subst10a        ; e = yes
  1001.     mov    cmptab,offset mcctab    ; main Macro table for \m(..)
  1002. subst10a:mov    cmsflg,0        ; see leading spaces/tabs
  1003.     mov    cmsiz,0            ; word size
  1004. subst11:call    cmgtch            ; read a character into ah
  1005.     jnc    subst13            ; nc = non-terminator
  1006.     cmp    ah,'?'            ; need help?
  1007.     jne    subst11a        ; ne = no
  1008.     jmp    cmkyhlp            ; do help and exit
  1009. subst11a:cmp    ah,escape        ; escape?
  1010.     jne    subst12            ; ne = no
  1011.     mov    bx,cmsptr        ; where word started
  1012.     mov    cmrptr,bx        ; omit word to date
  1013.     mov    cmwptr,bx
  1014.     call    cmkyesc            ; process escape
  1015.     mov    dx,1            ; signal valtoa to add trailing space
  1016.     jnc    subst14            ; nc = success (found the keyword)
  1017.                     ;
  1018. subst12:mov    bx,cmsptr        ; where word started
  1019.     sub    bx,3            ; back over "\v(" pr "\$(" part
  1020.     mov    cmrptr,bx        ; omit "\v(..." or "\$(..." to date
  1021.     mov    cmwptr,bx        ;  and "\m(..." too
  1022.     jmp    repars            ; reparse command without \v(...
  1023.                     ;
  1024. subst13:inc    cmsiz            ; count user chars
  1025.     cmp    ah,')'            ; end bracket?
  1026.     jne    subst11            ; ne = no, keep looking
  1027.     dec    cmsiz            ; omit user's ')' from tests
  1028.     cmp    subtype,'$'        ; \$(..)?
  1029.     je    subst13a        ; e = yes, no keyword in table
  1030.     call    getkw            ; \m(..) and \v(..) test for keyword
  1031.     jc    subst12            ; c = failure
  1032.     jmp    short subst13b        ; success
  1033.  
  1034. subst13a:call    envvar            ; search Environment for the word
  1035.     jc    subst12            ; c = failure
  1036.     mov    bx,offset envtab+1    ; Environment data structure
  1037.  
  1038. subst13b:mov    ax,valtmp        ; where word started
  1039.     mov    cmrptr,ax
  1040.     sub    ax,3            ; backup to "\v(" or "\$("
  1041.     mov    cmrptr,ax        ; write output where backslash was
  1042.     mov    cmwptr,ax
  1043.     mov    cx,[bx]            ; bx = structure pointer, cx=keyw len
  1044.     add    cx,2            ; skip count byte
  1045.     add    bx,cx            ; point at 16 bit value field
  1046.     mov    bx,[bx]            ; get value to bx for valtoa
  1047.     xor    dx,dx            ; signal valtoa to not add trailing sp
  1048. subst14:
  1049.     cmp    taklev,maxtak        ; room in take level?
  1050.     jb    subst15            ; b = yes
  1051.     mov    dx,offset stkmsg    ; out of work space msg
  1052.     mov    ah,prstr        ; display error message
  1053.     int    dos
  1054.     jmp    short subst17
  1055. subst15:push    di
  1056.     call    valtoa            ; make text be an internal macro
  1057.     jc    subst16            ; c = failed
  1058.     add    takadr,size takinfo    ; pointer to new Take structure
  1059.     inc    taklev            ; next Take level
  1060.     push    bx
  1061.     mov    bx,takadr        ; address of take structure
  1062.     mov    ax,ds
  1063.     mov    [bx].takbuf,ax        ; segment of Take buffer
  1064.     mov    [bx].takptr,offset valbuf+2 ; offset of beginning of def text
  1065.     mov    [bx].takcnt,di        ; # of chars in buffer
  1066.     mov    [bx].taktyp,0fdh    ; flag as an internal macro
  1067.     pop    bx
  1068. subst16:pop    di
  1069. subst17:pop    dx
  1070.     pop    cmhlp
  1071.     pop    cmsiz            ; restore borrowed keyword parameters
  1072.     pop    cmsptr
  1073.     pop    cmptab
  1074.     jmp    repars            ; reparse cmd line with new material
  1075. subst    endp
  1076.  
  1077. ; Make an internal macro defined as the text for one of the value variables.
  1078. ; Use incoming DX as trailing space suppression flag, if null.
  1079. valtoa    proc    near
  1080.     push    dx            ; save trailing space flag
  1081.     mov    di,offset valbuf+2    ; start text here
  1082.     mov    word ptr [di],0        ; fill buffer with sweet nothings
  1083.                     ; BX has index of variable
  1084.     cmp    bx,0            ; Environment?
  1085.     jne    valtoa1            ; ne = no
  1086.     mov    cx,envlen        ; string length
  1087.     jcxz    valtoa0            ; z = empty
  1088.     push    es
  1089.     push    ds
  1090.     mov    ax,ds
  1091.     mov    es,ax            ; destination is es:di
  1092.     lds    si,envadr        ; ds:si is source from Environment
  1093.     cld
  1094.     rep    movsb            ; copy string
  1095.     pop    ds            ; recover ds
  1096.     pop    es
  1097. valtoa0:jmp    valtoa20
  1098.     
  1099. valtoa1:cmp    bx,1            ; ARGC?
  1100.     jne    valtoa2            ; ne = no
  1101.     call    wrtargc            ; write argc
  1102.     jmp    valtoa20
  1103. valtoa2:cmp    bx,2            ; COUNT?
  1104.     jne    valtoa3            ; ne = no
  1105.     call    wrtcnt            ; write it
  1106.     jmp    valtoa20
  1107. valtoa3:cmp    bx,3            ; DATE?
  1108.     jne    valtoa4
  1109.     call    wrtdate
  1110.     jmp    valtoa20
  1111. valtoa4:cmp    bx,4            ; ERRORLEVEL?
  1112.     jne    valtoa5            ; ne = no
  1113.     call    wrterr
  1114.     jmp    valtoa20
  1115. valtoa5:cmp    bx,5            ; DIR?
  1116.     jne    valtoa6
  1117.     call    wrtdir
  1118.     jmp    valtoa20
  1119. valtoa6:cmp    bx,6            ; TIME?
  1120.     jne    valtoa7
  1121.     call    wrttime
  1122.     jmp    valtoa20
  1123. valtoa7:cmp    bx,7            ; VERSION?
  1124.     jne    valtoa8            ; ne = no
  1125.     mov    ax,version        ; get version such as 300
  1126.     call    fdec2di            ; convert binary to asciiz
  1127.     mov    word ptr [di],0020h    ; space, null
  1128.     inc    di
  1129.     jmp    valtoa20
  1130. valtoa8:cmp    bx,8            ; PLATFORM?
  1131.     jne    valtoa9            ; ne = no
  1132.     call    wrtplat            ; get machine name, e.g. "IBM-PC"
  1133.     jmp    valtoa20
  1134. valtoa9:cmp    bx,9            ; SYSTEM?
  1135.     jne    valtoa10        ; ne = no
  1136.     call    wrtsystem        ; get "MS-DOS" string
  1137.     jmp    valtoa20
  1138. valtoa10:cmp    bx,10            ; KEYBOARD?
  1139.     jne    valtoa11        ; ne = no
  1140.     call    wrtkbd            ; 88 or 101 value
  1141.     jmp    valtoa20
  1142. valtoa11:cmp    bx,11            ; SPEED?
  1143.     jne    valtoa12        ; ne = no
  1144.     push    di
  1145.     call    fgetbaud        ; read baud rate from hardware
  1146.     pop    di
  1147.     mov    bx,portval
  1148.     mov    ax,[bx].baud
  1149.     cmp    al,byte ptr bdtab    ; index versus number of table entries
  1150.     jb    valtoa11a        ; b = index is in the table
  1151.     mov    si,offset cmunk-2    ; unrecognized value, say "unknown"
  1152.     mov    bx,7            ; length of string
  1153.     jmp    short valtoa11c
  1154. valtoa11a:mov    si,offset bdtab        ; ascii rate table
  1155.     mov    cl,[si]            ; number of entries
  1156.     inc    si            ; point to an entry
  1157. valtoa11b:
  1158.     mov    bx,[si]            ; length of text string
  1159.     cmp    ax,[si+bx+2]        ; our index vs table entry index
  1160.     je    valtoa11c        ; e = match
  1161.     add    si,bx            ; skip text
  1162.     add    si,4            ; skip count and index word
  1163.     loop    valtoa11b        ; look again
  1164.     mov    si,offset cmunk-2    ; unrecognized value, say "unknown"
  1165.     mov    bx,7            ; length of string
  1166. valtoa11c:mov    cx,bx            ; length of string
  1167.     add    si,2            ; point at string
  1168.     rep    movsb            ; copy string
  1169.     jmp    short valtoa20
  1170. valtoa12:cmp    bx,12            ; PROGRAM?
  1171.     jne    valtoa13        ; ne = no
  1172.     call    wrtprog            ; get "MS-DOS_KERMIT" string
  1173.     jmp    short valtoa20
  1174. valtoa13:cmp    bx,13            ; STATUS?
  1175.     jne    valtoa14        ; ne = no
  1176.     call    wrtstat            ; compose status string
  1177.     jmp    short valtoa20
  1178. valtoa14:cmp    bx,14            ; NDATE?
  1179.     jne    valtoa15        ; ne = no
  1180.     call    wrtndate
  1181.     jmp    short valtoa20
  1182. valtoa15:cmp    bx,15            ; LINE, PORT?
  1183.     jne    valtoa19        ; ne = no
  1184.     call    wrtport
  1185.     jmp    short valtoa20
  1186. valtoa19:pop    dx            ; assume an internal Macro
  1187.     add    takadr,size takinfo    ; pointer to new Take structure
  1188.     inc    taklev            ; next Take level
  1189.     push    bx
  1190.     mov    ax,bx            ; save seg of string here
  1191.     mov    bx,takadr        ; address of take structure
  1192.     mov    [bx].takbuf,ax        ; segment of Take buffer
  1193.     mov    [bx].takptr,2         ; offset of beginning of def text
  1194.     push    es
  1195.     mov    es,ax            ; segment of string definition
  1196.     mov    di,es:[0]        ; get length of string text (count)
  1197.     pop    es
  1198.     mov    [bx].takcnt,di        ; # of chars in buffer
  1199.     mov    [bx].taktyp,0fdh    ; flag as an internal macro
  1200.     pop    bx
  1201.     jmp    short valtoax        ; exit early with CARRY SET(!)
  1202.  
  1203. valtoa20:pop    dx            ; trailing space flag
  1204.     or    dx,dx            ; leave the spaces?
  1205.     jnz    valtoa21        ; nz = yes
  1206.     cmp    word ptr [di-1],0020h    ; trailing space?
  1207.     jne    valtoa21        ; ne = no
  1208.     dec    di            ; remove space
  1209. valtoa21:sub    di,offset valbuf+2    ; di = length of the buffer contents
  1210.     clc
  1211.     ret
  1212. valtoax:stc
  1213.     ret
  1214. valtoa    endp
  1215.  
  1216. ; Far callable version
  1217. fvaltoa    proc    far
  1218.     call    valtoa
  1219.     ret
  1220. fvaltoa    endp
  1221. ; Set envadr to the string following the <variable=> keyword in the
  1222. ; Environment and set envlen to its length after removing leading and
  1223. ; trailing whitespace.
  1224. ; <variable> starts at ds:<valtmp>, of length cmsiz-1, and can be mixed case.
  1225. ; Return carry set if can't find the <variable=> line in the Environment.
  1226. envvar    proc    near
  1227.     push    es
  1228.     mov    bx,valtmp        ; start of variable name
  1229.     mov    cx,cmsiz        ; length of variable name, w/o ')'
  1230.     or    cx,cx            ; empty?
  1231.     jle    envvar3            ; le = nothing to look for, fail
  1232.     push    bx
  1233.     push    cx
  1234. envvar1:mov    al,byte ptr [bx]    ; scan variable name in our buffer
  1235.     cmp    al,'a'            ; lower case
  1236.     jb    envvar2            ; b = no
  1237.     cmp    al,'z'            ; still in lower case range?
  1238.     ja    envvar2            ; a = no
  1239.     and    al,not 20h        ; convert to DOS's upper case
  1240.     mov    byte ptr [bx],al    ; replace char
  1241. envvar2:inc    bx
  1242.     loop    envvar1
  1243.     pop    cx
  1244.     pop    bx            ; find "<variable>=" in Environment
  1245.     call    fgetenv            ; dx = offset in Environment of "="
  1246.     jnc    envvar4            ; nc = success
  1247. envvar3:pop    es            ; no such variable
  1248.     stc                ; c = failure
  1249.     ret
  1250. ; dx has offset in Environment of char "="
  1251. ; ds:valtmp is start of variable name, cmsiz is length + 1 of the name.
  1252. ; Return seg:offset and length variables so we can copy from there in valtoa
  1253. envvar4:push    di
  1254.     push    si
  1255.     xor    ax,ax
  1256.     mov    word ptr envadr,ax    ; offset in env of variable's string
  1257.     mov    word ptr envadr+2,ax    ; seg of same
  1258.     mov    envlen,ax        ; length of string
  1259.     mov    es,psp            ; our Prog Seg Prefix segment
  1260.     mov    ax,es:word ptr[env]    ; pick up Environment address
  1261.     mov    es,ax            ; set es: to Environment segment
  1262.     mov    di,dx            ; line scan pointer
  1263.     cmp    byte ptr es:[di],'='    ; did we stop on this?
  1264.     jne    envvar5            ; ne = no
  1265.     inc    di            ; skip the "=" char
  1266. envvar5:mov    al,es:[di]        ; scan over leading white space
  1267.     inc    di
  1268.     or    al,al            ; end of line terminator?
  1269.     jz    envvarf            ; z = yes, fail
  1270.     cmp    al,TAB            ; HT?
  1271.     jne    envvar6            ; ne = no
  1272.     mov    al,' '            ; HT becomes a space
  1273. envvar6:cmp    al,' '            ; white space?
  1274.     je    envvar5            ; scan off white space
  1275.     dec    di            ; backup to non-white char
  1276.     mov    word ptr envadr,di    ; offset of string in Environment
  1277.     mov    word ptr envadr+2,es    ; seg of string in Environment
  1278.     mov    si,di            ; remember starting offset here
  1279.                     ; remove trailing spaces from string
  1280.     xor    al,al            ; a null
  1281.     mov    cx,127            ; max length to search
  1282.     cld
  1283.     repne    scasb            ; skip over non-nulls
  1284.     dec    di            ; backup to null
  1285.     dec    di            ; backup to last string char
  1286.     mov    cx,di            ; ending offset
  1287.     inc    cx            ; count the char
  1288.     sub    cx,si            ; minus starting offset yields length
  1289.     jcxz    envvar9            ; z = empty string
  1290. envvar7:mov    al,es:[di]        ; last char
  1291.     dec    di            ; backup one char
  1292.     cmp    al,' '            ; space?
  1293.     je    envvar8            ; e = yes
  1294.     cmp    al,TAB            ; HT?
  1295.     jne    envvar9            ; ne = no, end of white space
  1296. envvar8:loop    envvar7            ; keep looking
  1297. envvar9:mov    envlen,cx        ; store the length
  1298.     clc                ; say success
  1299.     jmp    short envvarx
  1300. envvarf:stc                ; say failure
  1301. envvarx:pop    si
  1302.     pop    di
  1303.     pop    es
  1304.     ret
  1305. envvar    endp
  1306.  
  1307. ; Read chars from Take file, keyboard, or redirected stdin. Edit and remove
  1308. ; BS & DEL, Tab becomes space, act on Control-C, pass Control-U and Control-W.
  1309. ; Do echoing unless comand.cmquiet is non-zero. Do semicolon comments in Take
  1310. ; and indirect stdin files (\; means literal semicolon). Return char in AL.
  1311. CMGETC    proc    near            ; Basic raw character reader
  1312. cmget01:cmp    prevch,0        ; left over char yet to be exported?
  1313.     je    cmget02            ; e = no
  1314.     mov    al,prevch        ; get old char
  1315.     mov    prevch,0        ; clear storage
  1316.     jmp    cmget6            ; analyze it
  1317. cmget02:cmp    taklev,0        ; in a Take file?
  1318.     jne    cmget1            ; ne = yes, do Take reader section
  1319.     call    fisdev            ; is stdin a device or a file?
  1320.     jnc    cmget20            ; nc = file (redirection of stdin)
  1321.     jmp    cmget10            ; c = device, do separately
  1322.  
  1323. cmget20:call    fiseof            ; see if file is empty
  1324.     jc    cmget21            ; c = EOF on disk file
  1325.     mov    ah,coninq        ; read the char from file, not device
  1326.     int    dos
  1327.     cmp    al,cr            ; is it a cr?
  1328.     je    cmget01            ; yes, ignore and read next char
  1329.     cmp    al,ctlz            ; Control-Z?
  1330.     je    cmget21            ; e = yes, same as EOF here
  1331.     cmp    al,lf            ; LF's end lines from disk files
  1332.     jne    cmget8            ; ne = not LF, pass along as is
  1333.     mov    al,cr            ; make LF a CR for this parser
  1334.     call    fiseof            ; see if this is the last char in file
  1335.     jnc    cmget8            ; nc = not EOF, process new CR
  1336. cmget21:mov    flags.extflg,1        ; EOF on disk file, set exit flag
  1337.     jmp    short cmget6        ; do echoing and return
  1338.  
  1339. cmget1:    push    bx            ; read from Take file
  1340.     mov    bx,takadr        ; offset of this Take structure
  1341.     cmp    [bx].takcnt,0        ; bytes remaining in Take buffer
  1342.     jne    cmget4            ; ne = not empty
  1343.     cmp    [bx].taktyp,0feh    ; type of Take (file?)
  1344.     jne    cmget3            ; ne = no (macro)
  1345.     call    ftakrd            ; read another buffer
  1346.     cmp    [bx].takcnt,0        ; anything in the buffer?
  1347.     jne    cmget4            ; ne = yes
  1348. cmget3:    pop    bx            ; clear stack
  1349.     jmp    short cmget5        ; close take file
  1350.  
  1351. cmget4:    push    si
  1352.     push    es
  1353.     mov    es,[bx].takbuf        ; segment of Take buffer
  1354.     mov    si,[bx].takptr        ; current offset in Take buffer
  1355.     mov    al,es:[si]        ; read a char from Take buffer
  1356.     inc    si
  1357.     mov    [bx].takptr,si        ; move buffer pointer
  1358.     dec    [bx].takcnt        ; decrease number of bytes remaining
  1359.     pop    es
  1360.     pop    si
  1361.     pop    bx
  1362.     cmp    al,ctlz            ; Control-Z?
  1363.     jne    cmget6            ; ne = no
  1364. cmget5:    push    bx
  1365.     mov    bx,takadr        ; offset of this Take structure
  1366.     mov    ah,[bx].taktyp        ; save kind of Take/macro
  1367.     pop    bx
  1368.     cmp    ah,0fdh            ; internal macro?
  1369.     je    cmget5b            ; e = yes, close but no auto-CR
  1370.     cmp    comand.cmkeep,0        ; keep Take/macro open after eof?
  1371.     jne    cmget5a            ; ne = yes
  1372. cmget5b:push    ax
  1373.     call    ftakclos        ; close take file
  1374.     pop    ax
  1375.     cmp    ah,0fdh            ; internal macro?
  1376.     jne    cmget5a            ; ne = no, file or regular macro
  1377.     jmp    cmgetc            ; internal macros have no auto CR
  1378. cmget5a:mov    al,cr            ; report cr as last char
  1379.     mov    noparse,0        ; and say end of comment
  1380.                     ; start common code
  1381. cmget6:
  1382.     cmp    al,lf            ; line feed?
  1383.     jne    cmget8            ; ne = no
  1384.     jmp    cmget01            ; yes, ignore and read another char
  1385.                     ; handle comments (echo but not parse)
  1386. cmget8:    cmp    noparse,0        ; parsing?
  1387.     jne    cmget9            ; ne = yes, do echo and no parse
  1388.     cmp    prevch,0        ; have previous char to analyze?
  1389.     jne    cmget8c            ; ne = yes
  1390.     cmp    taklev,0        ; in a Take file or Macro?
  1391.     je    cmget8e            ; e = no, use ";" as a literal
  1392.     push    bx            ; treat ";" as literal in int macros
  1393.     mov    bx,takadr        ; offset of this Take structure
  1394.     cmp    [bx].taktyp,0fdh    ; internal macro?
  1395.     pop    bx
  1396.     je    cmget8e            ; e = yes, semicolons are literals
  1397.     cmp    al,'\'            ; start of '\;'?
  1398.     jne    cmget8a            ; ne = no
  1399.     mov    prevch,al        ; yes, maybe. save '\' til later
  1400.     jmp    cmget02            ; read next char
  1401. cmget8a:cmp    al,';'            ; possible start of comment?
  1402.     jne    cmget8e            ; no, export al
  1403.     mov    al,' '            ; replace ';' with space for comment
  1404.     jmp    short cmget9        ; go start a comment
  1405.  
  1406. cmget8c:cmp    al,';'            ; end of '\;'?
  1407.     je    cmget8d            ; e = yes, omit leading backslash
  1408.     xchg    prevch,al        ; no, save new, recover old '\'
  1409.     jmp    short cmget8e        ; export '\'
  1410. cmget8d:mov    prevch,0        ; clear old '\'
  1411. cmget8e:jmp    short cmget12        ; do parsing
  1412.  
  1413.                     ; echo comment
  1414. cmget9:    cmp    flags.takflg,0        ; echoing take files?
  1415.     je    cmget9a            ; e = no
  1416.     push    ax
  1417.     push    dx
  1418.     mov    ah,conout        ; echo current char
  1419.     mov    dl,al
  1420.     int    dos
  1421.     pop    dx
  1422.     pop    ax
  1423. cmget9a:mov    noparse,0        ; cr ends comment
  1424.     cmp    al,cr            ; end of comment?
  1425.     je    cmget13            ; e = yes, export cr
  1426.     mov    noparse,1        ; still in comment
  1427.     jmp    cmget01            ; read more chars
  1428.  
  1429.                     ; read from tty device
  1430. cmget10:mov    ah,coninq        ; Get a char from device, not file
  1431.     int    dos            ;  with no echoing
  1432.     or    al,al
  1433.     jnz    cmget12            ; ignore null bytes of special keys
  1434.     int    dos            ; read and discard scan code byte
  1435.     jmp    short cmget10        ; try again
  1436.  
  1437. cmget12:cmp    al,'C'and 1Fh        ; Control-C?
  1438.     je    cmget14            ; e = yes
  1439.     cmp    al,TAB            ; tab is replaced by space
  1440.     jne    cmget13            ; ne = not tab
  1441.     mov    al,' '
  1442.     ret
  1443. cmget13:cmp    al,LF            ; LF becomes CR interactively
  1444.     jne    cmget13a
  1445.     mov    al,CR
  1446. cmget13a:ret                ; normal exit, char is in AL
  1447.  
  1448. cmget14:mov    ah,prstr        ; Control-C handler
  1449.     push    dx
  1450.     mov    dx,offset ctcmsg    ; show Control-C
  1451.     int    dos
  1452.     pop    dx
  1453.     mov    prevch,0
  1454.     mov    flags.cxzflg,'C'    ; tell others the news
  1455.     jmp    dword ptr pcmexit    ; fail immediately via longjmp
  1456. cmgetc    endp
  1457.  
  1458. ; Read chars from user (cmgetc). Detect terminators. Reads from buffer
  1459. ; cmbuf. Set read pointer comand.cmrptr to next free buffer byte if
  1460. ; char is not a terminator: chars CR, LF, FF, '?' (returns carry set for
  1461. ; terminators). Do ^U, ^W editing, convert FF to CR plus clear screen.
  1462. ; Edit "-<cr>" as line continuation, "\-<cr>" as "-<end of line>".
  1463. ; Return char in AH.
  1464. CMINBF    proc    near            ; Buffer reader, final editor
  1465.     cmp    cmwptr,offset cmdbuf+size cmdbuf-3 ; max buffer size - 3
  1466.     jb    cminb1            ; b = not full for writing
  1467.     mov    ah,conout        ; almost full, notify user
  1468.     push    dx
  1469.     mov    dl,bell
  1470.     int    dos
  1471.     pop    dx
  1472.     cmp    cmrptr,offset cmdbuf+size cmdbuf ; reading beyond buffer?
  1473.     jb    cminb1            ; b = no
  1474.     mov    ah,prstr
  1475.     mov    dx,offset cmer09    ; command too long
  1476.     int    dos
  1477.     jmp    prserr            ; overflow = parse error
  1478.  
  1479. cminb1:    push    bx
  1480.     mov    bx,cmrptr        ; read pointer
  1481.     mov    ah,[bx]            ; get current command char while here
  1482.     cmp    bx,cmwptr        ; do we need to read more?
  1483.     pop    bx            ; no if cmrptr < cmwptr
  1484.     jb    cminb2            ; b: cmrptr < cmwptr (have extra here)
  1485.     call    cmgetc            ; no readahead, read another into al
  1486.     mov    ah,al            ; keep char in 'ah'
  1487.     push    bx
  1488.     mov    bx,cmwptr        ; get the pointer into the buffer
  1489.     mov    [bx],ah            ; put it in the buffer
  1490.     inc    bx
  1491.     mov    cmwptr,bx        ; inc write pointer
  1492.     pop    bx
  1493.                     ; Char to be delivered is in ah
  1494. cminb2:    cmp    ah,'W' and 1fh        ; is it a ^W?
  1495.     jne    cminb3
  1496.     call    cntrlw            ; kill the previous word
  1497.     jnc    cminbf            ; nc = no change, get another char
  1498.     jmp    repars            ; need a new command scan (cleans stk)
  1499.  
  1500. cminb3:    cmp    ah,'U' and 1fh        ; is it a ^U?
  1501.     jne    cminb3a            ; ne = no
  1502.     mov    cmwptr,offset cmdbuf    ; reset buffer write pointer
  1503.     jmp    repars            ; go start over (cleans stack)
  1504.                     ; BS and DEL
  1505. cminb3a:cmp    ah,DEL            ; delete code?
  1506.     je    cminb3b            ; e = yes
  1507.     cmp    ah,BS            ; Backspace (a delete operator)?
  1508.     jne    cminb4            ; ne = no
  1509. cminb3b:call    bufdel            ; delete char from buffer
  1510.     jc    cminb3c            ; c = did erasure
  1511.     jmp    cminbf            ; no erasure, ignore BS, get more
  1512. cminb3c:jmp    repars            ; could have deleted previous token
  1513.  
  1514. cminb4:    push    bx            ; look for hyphen or \hyphen
  1515.     cmp    ah,cr            ; check for hyphen line continuation
  1516.     jne    cminb4b            ; ne = not end of line
  1517.     mov    bx,cmwptr        ; get the pointer into the buffer
  1518.     cmp    bx,offset cmdbuf-2    ; do we have a previous char?
  1519.     jb    cminb4b            ; b = no
  1520.     cmp    byte ptr[bx-2],'-'    ; previous char was a hyphen?
  1521.     jne    cminb4b            ; ne = no
  1522.     pop    bx
  1523.     call    bufdel            ; delete the hyphen
  1524.     jmp    repars
  1525. cminb4b:pop    bx
  1526.                     ; Echoing done here
  1527.     cmp    comand.cmquiet,0    ; quiet mode?
  1528.     jne    cminb5            ; yes, skip echoing
  1529.     cmp    taklev,0        ; in a take file?
  1530.     je    cminb4a            ; e = no
  1531.     cmp    flags.takflg,0        ; echo take file?
  1532.     je    cminb5            ; e = no
  1533. cminb4a:push    ax            ; save the char
  1534.     cmp    ah,' '            ; printable?
  1535.     jae    cminb4c            ; yes, no translation needed
  1536.     cmp    ah,cr            ; this is printable
  1537.     je    cminb4c
  1538.     cmp    ah,lf
  1539.     je    cminb4c
  1540.     cmp    ah,escape        ; escape?
  1541.     je    cminb4d            ; do not echo this character
  1542.     push    ax            ; show controls as caret char
  1543.     push    dx
  1544.     mov    dl,5eh            ; caret
  1545.     mov    ah,conout
  1546.     int    dos
  1547.     pop    dx
  1548.     pop    ax
  1549.     add    ah,'A'-1        ; make control code printable
  1550. cminb4c:push    dx
  1551.     mov    dl,ah
  1552.     mov    ah,conout
  1553.     int    dos            ; echo it ourselves
  1554.     pop    dx
  1555. cminb4d:pop    ax            ; and return char in ah
  1556.  
  1557. cminb5:    cmp    ah,cr            ; carriage return?
  1558.     je    cminb6
  1559.     cmp    ah,lf            ; line feed?
  1560.     je    cminb6
  1561.     cmp    ah,ff            ; formfeed?
  1562.     jne    cminb7            ; none of the above, report bare char
  1563.     call    fcmblnk            ; FF: clear the screen and
  1564.     push    bx
  1565.     push    cx
  1566.     push    dx
  1567.     call    flocate            ; Home the cursor
  1568.     mov    bx,cmwptr        ; make the FF parse like a cr
  1569.     mov    byte ptr [bx-1],cr    ; pretend a carriage return were typed
  1570.     pop    dx
  1571.     pop    cx
  1572.     pop    bx
  1573. cminb6: cmp    cmwptr,offset cmdbuf    ; parsed any chars yet?
  1574.     jne    cminb7            ; ne = yes
  1575.     cmp    comand.cmcr,0        ; bare cr's allowed?
  1576.     jne    cminb7            ; ne = yes
  1577.     jmp    prserr            ; If not, just start over
  1578. cminb7:    clc
  1579.     ret
  1580. cminbf    endp
  1581.  
  1582. ; Read chars from cminbf. Cmrptr points to next char to be read.
  1583. ; Compresses repeated spaces if cmsflg is non-zero. Exit with cmrptr pointing
  1584. ; at a terminator or otherwise at next free slot.
  1585. ; Non-space then space acts as a terminator but cmrptr is incremented.
  1586. ; Substitution variables, '\%x', are detected and expanded. Return char in AH.
  1587.  
  1588. CMGTCH    proc    near            ; return char in AH, from rescan buf
  1589.     call    cminbf            ; get char from buffer or user
  1590.     push    bx
  1591.     mov    bx,cmrptr        ; get read pointer into the buffer
  1592.     mov    ah,[bx]            ; read the next char
  1593.     inc    bx
  1594.     mov    cmrptr,bx        ; where to read next time
  1595.     pop    bx
  1596.     call    subst            ; examine for text substitution
  1597.     jnc    cmgtc1            ; nc = no substitutions done
  1598.     jmp    repars            ; reparse line with new material
  1599.  
  1600. cmgtc1:    cmp    ah,' '            ; space?
  1601.     jne    cmgtc3            ; ne = no
  1602.     cmp    bracecnt,0        ; are we within braces?
  1603.     jne    cmgtc3            ; ne = yes, treat space as literal
  1604.     cmp    cmsflg,0        ; space flag, was last char a space?
  1605.     jne    cmgtch            ; ne = yes, get another char
  1606.     mov    cmsflg,0FFH        ; Set the space(s)-seen flag
  1607.     mov    ah,' '            ; character for caller
  1608.     stc                ; set carry for terminator
  1609.     ret                ; return space as a terminator
  1610. cmgtc3: mov    cmsflg,0        ; clear the space-seen flag
  1611.     cmp    ah,braceop        ; opening brace?
  1612.     jne    cmgtc3b            ; ne = no
  1613.     inc    bracecnt        ; count it
  1614.     jmp    short cmgtc3c
  1615. cmgtc3b:cmp    ah,bracecl        ; closing brace?
  1616.     jne    cmgtc3c            ; ne = no
  1617.     sub    bracecnt,1        ; count down and get a sign bit
  1618.     jns    cmgtc3c            ; ns = no underflow
  1619.     mov    bracecnt,0        ; catch underflows
  1620. cmgtc3c:cmp    ah,escape        ; terminators remain in buffer but
  1621.     je    cmgtc4            ;  are ready to be overwritten
  1622.     cmp    ah,'?'            ; is the user curious?
  1623.     jne    cmgtc3a            ; ne = no
  1624.     cmp    taklev,0        ; in a Take file?
  1625.     jne    cmgtc3d            ; ne = yes, make query ordinary char
  1626.     je    cmgtc4
  1627. cmgtc3a:cmp    ah,cr
  1628.     je    cmgtc4
  1629.     cmp    ah,lf
  1630.     je    cmgtc4
  1631.     cmp    ah,ff
  1632.     je    cmgtc4
  1633. cmgtc3d:clc                ; carry clear for non-terminator
  1634.     ret
  1635. cmgtc4: dec    cmrptr            ; point at terminating char
  1636.     stc                ; set carry to say it is a terminator
  1637.     ret
  1638. cmgtch    endp
  1639.  
  1640. ; Reset comand.cmdbuf write pointer (cmwptr) to where the read pointer
  1641. ; (cmrptr) is now. Discards material not yet read.
  1642. bufreset proc    near
  1643.     push    cmrptr            ; where next visible char is read
  1644.     push    ax            ; count removed curly braces
  1645.     push    si
  1646.     mov    si,cmrptr        ; where to look
  1647.     mov    cx,cmwptr        ; last place being removed
  1648.     sub    cx,si            ; length to examine
  1649.     cld
  1650. bufres1:lodsb
  1651.     cmp    al,braceop        ; opening brace, counted already?
  1652.     jne    bufres2            ; ne = no
  1653.     dec    bracecnt        ; uncount it
  1654.     jmp    short bufres3
  1655. bufres2:cmp    al,bracecl        ; closing brace, counted already?
  1656.     jne    bufres3            ; jne = no
  1657.     inc    bracecnt        ; uncount it
  1658. bufres3:loop    bufres1
  1659.     cmp    bracecnt,0        ; negative?
  1660.     jge    bufres4            ; ge = no
  1661.     mov    bracecnt,0
  1662. bufres4:pop    si
  1663.     pop    ax
  1664.     pop    cmwptr            ; where new char goes in buffer
  1665.     ret
  1666. bufreset endp
  1667.  
  1668. ; Delete character from screen and adjust buffer. Returns carry clear if
  1669. ; no erasure, carry set otherwise.
  1670. bufdel    proc    near
  1671.     push    ax
  1672.     push    si
  1673.     mov    si,cmrptr
  1674.     mov    al,[si]
  1675.     cmp    al,braceop        ; opening brace, counted already?
  1676.     jne    bufdel1            ; ne = no
  1677.     dec    bracecnt        ; uncount it
  1678.     jmp    short bufdel2
  1679. bufdel1:cmp    al,bracecl        ; closing brace?
  1680.     jne    bufdel2            ; ne = no
  1681.     inc    bracecnt        ; uncount it
  1682. bufdel2:cmp    bracecnt,0        ; negative?
  1683.     jge    bufdel3            ; ge = no
  1684.     mov    bracecnt,0
  1685. bufdel3:pop    si
  1686.     pop    ax
  1687.     dec    cmrptr            ; remove previous char from buffer
  1688.     cmp    cmrptr,offset cmdbuf    ; back too far?
  1689.     jae    bufde2            ; ae = no, material can be erased
  1690.     mov    cmrptr,offset cmdbuf    ; set to start of buffer
  1691.     call    bufreset        ; reset buffer
  1692.     mov    bracecnt,0        ; ensure this is now cleared
  1693.     clc                ; say no erasure
  1694.     ret
  1695. bufde2:    call    bufreset        ; reset buffer
  1696.     stc                ; say did erasure
  1697.     ret
  1698. bufdel    endp
  1699.  
  1700. ; Come here is user types ^W when during input. Remove word from buffer.
  1701. cntrlw    proc    near
  1702.     push    ax
  1703.     push    cx
  1704.     push    dx
  1705.     call    bufreset        ; truncate buffer at cmrptr
  1706.     mov    cx,cmrptr        ; read pointer
  1707.     sub    cx,offset cmdbuf    ; compute chars in buffer
  1708.     clc                ; say have not yet modified line
  1709.     jcxz    ctlw2            ; z = nothing to do, exit no-carry
  1710.     push    es
  1711.     std                ; scan backward
  1712.     mov    ax,ds
  1713.     mov    es,ax            ; point to the data are
  1714.     mov    di,cmwptr        ; looking from here
  1715.     dec    di
  1716.     mov    al,' '
  1717.     repe    scasb            ; look for non-space
  1718.     je    ctlw1            ; all spaces, nothing to do
  1719.     inc    di            ; move back to non-space
  1720.     inc    cx
  1721.     repne    scasb            ; look for a space
  1722.     jne    ctlw1            ; no space, leave ptrs alone
  1723.     inc    di
  1724.     inc    cx            ; skip back over space
  1725. ctlw1:    inc    di
  1726.     pop    es
  1727.     cld                ; reset    direction flag
  1728.     mov    cmwptr,di        ; update pointer
  1729.     stc                ; set carry to say modified line
  1730. ctlw2:    pop    dx
  1731.     pop    cx
  1732.     pop    ax
  1733.     ret
  1734. cntrlw    endp
  1735.  
  1736. ; Jump to REPARS to do a rescan of the existing buffer.
  1737. ; Jump to PRSERR on a parsing error (quits command, clears old read material)
  1738.  
  1739. PRSERR    PROC NEAR
  1740.     mov    cmwptr,offset cmdbuf    ; initialize write pointer
  1741.     mov    ah,prstr
  1742.     mov    dx,offset crlf        ; leave old line, start a new one
  1743.     int    dos
  1744.     call    rprompt            ; restore master prompt level
  1745.                     ; reparse current line
  1746. REPARS:    mov    cmrptr,offset cmdbuf    ; reinit read pointer
  1747.     mov    comand.cmper,0        ; reset to variable recognition
  1748.     mov    cmsflg,0ffh        ; strip leading spaces
  1749.     mov    subcnt,0        ; clear substitution state variables
  1750.     mov    subtype,0
  1751.     mov    bracecnt,0
  1752.     cmp    taklev,0        ; in Take cmd?
  1753.     je    prser2            ; e = no
  1754.     cmp    flags.takflg,0        ; echo contents of Take file?
  1755.     je    prser3            ; e = no
  1756. prser2:    call    fctlu            ; clear display's line, reuse it
  1757.     mov    dx,comand.cmprmp    ; display the asciiz prompt
  1758.     call    fprtasz
  1759. prser3:    mov    bx,0ffffh        ; returned keyword value
  1760.     mov    sp,comand.cmostp    ; set new sp to old one
  1761.     jmp    dword ptr comand.cmrprs    ; jump to just before the prompt call
  1762. PRSERR    ENDP
  1763.  
  1764. ; Restore prompt material to that of the master prompt. This removes settings
  1765. ; of local PROMPT calls so we can reprompt at the main Kermit level.
  1766. RPROMPT    proc    near
  1767.     push    ax
  1768.     mov    ax,mcmprmp        ; address of prompt string
  1769.     or    ax,ax            ; any address given yet?
  1770.     jz    rprompt1        ; z = none, not inited yet
  1771.     mov    comand.cmprmp,ax    ; set current address ptr
  1772.     mov    ax,word ptr mcmrprs    ; offset of reparse address
  1773.     mov    word ptr comand.cmrprs,ax
  1774.     mov    ax,word ptr mcmrprs+2    ; segment of reparse address
  1775.     mov    word ptr comand.cmrprs+2,ax
  1776.     mov    ax,mcmostp        ; stack ptr at reparse time
  1777.     mov    comand.cmostp,ax
  1778. rprompt1:pop    ax
  1779.     ret
  1780. RPROMPT    endp
  1781.  
  1782.  
  1783.  
  1784. ; write \v(ARGC) contents to ds:di
  1785. wrtargc    proc    near
  1786.     xor    ax,ax
  1787.     cmp    taklev,0        ; in a Take/Macro?
  1788.     je    wrtarg1            ; e = no
  1789.     mov    bx,takadr        ; current Take structure
  1790.     mov    ax,[bx].takargc        ; get ARGC
  1791. wrtarg1:call    fdec2di            ; write as ascii
  1792.     mov    word ptr [di],0020h    ; space and null terminator
  1793.     inc    di
  1794.     ret
  1795. wrtargc    endp
  1796.  
  1797. ; write \v(COUNT) text to ds:di
  1798. wrtcnt    proc    near
  1799.     xor    ax,ax
  1800.     cmp    taklev,0        ; in a Take/Macro?
  1801.     je    wrtcnt1            ; e = no
  1802.     mov    bx,takadr        ; current Take structure
  1803.     mov    ax,[bx].takctr        ; get COUNT
  1804. wrtcnt1:call    fdec2di            ; write as ascii
  1805.     mov    word ptr [di],0020h    ; space and null terminator
  1806.     inc    di
  1807.     ret
  1808. wrtcnt    endp
  1809.  
  1810. ; write \v(DATE) text to ds:di
  1811. wrtdate    proc    near
  1812.     push    cx
  1813.     push    dx
  1814.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  1815.     int    dos
  1816.     xor    ah,ah
  1817.     cmp    tdfmt,0            ; USA standard mm/dd/yyyy?
  1818.     jne    wrtdat1            ; ne = no
  1819.     mov    al,dh            ; month
  1820.     call    wrtdat5            ; output
  1821.     mov    byte ptr [di],'/'    ; separate
  1822.     inc    di
  1823.     mov    al,dl            ; day
  1824.     call    wrtdat5
  1825.     mov    byte ptr [di],'/'
  1826.     inc    di
  1827.     mov    ax,cx
  1828.     jmp    short wrtdat3
  1829.  
  1830. wrtdat1:cmp    tdfmt,1            ; European standard dd/mm/yyyy?
  1831.     jne    wrtdat2            ; ne = no
  1832.     xor    ah,ah
  1833.     mov    al,dl            ; day
  1834.     call    wrtdat5
  1835.     mov    byte ptr [di],'/'
  1836.     inc    di
  1837.     mov    al,dh            ; month
  1838.     call    wrtdat5
  1839.     mov    byte ptr [di],'/'
  1840.     inc    di
  1841.     mov    ax,cx
  1842.     jmp    short wrtdat3
  1843.  
  1844. wrtdat2:mov    ax,cx            ; Japan yyyy:mm:dd,   year
  1845.     call    wrtdat5
  1846.     mov    byte ptr [di],':'
  1847.     inc    di
  1848.     xor    ah,ah
  1849.     mov    al,dh            ; month
  1850.     call    wrtdat5
  1851.     mov    byte ptr [di],':'
  1852.     inc    di
  1853.     mov    al,dl            ; day
  1854. wrtdat3:call    wrtdat5
  1855.     mov    word ptr [di],0020h    ; space and null terminator
  1856.     inc    di
  1857.     pop    dx
  1858.     pop    cx
  1859.     ret
  1860.     ret
  1861.  
  1862. wrtdat5:cmp    ax,10            ; leading tens digit present?
  1863.     jae    wrtdat6            ; ae = yes
  1864.     mov    byte ptr [di],'0'    ; insert leading 0
  1865.     inc    di
  1866. wrtdat6:call    fdec2di            ; write decimal asciiz to buffer
  1867.     ret
  1868.  
  1869. wrtdate    endp
  1870.  
  1871. ; write \v(ERRORLEVEL) text to ds:di
  1872. wrterr    proc    near
  1873.     mov    al,errlev        ; current Errorlevel
  1874.     xor    ah,ah
  1875.     call    fdec2di            ; write as ascii
  1876.     mov    word ptr [di],0020h    ; space and null terminator
  1877.     inc    di
  1878.     ret
  1879. wrterr    endp
  1880.  
  1881. ; write \v(KEYBOARD) text to ds:di
  1882. wrtkbd    proc    near
  1883.     mov    ax,keyboard        ; 88 or 101 keyboard keys
  1884.     call    fdec2di            ; write as ascii
  1885.     mov    word ptr [di],0020h    ; space and null terminator
  1886.     inc    di
  1887.     ret
  1888. wrtkbd    endp
  1889.  
  1890. ; write \v(NDATE) text to ds:di
  1891. ; where NDATE is YYYYMMDD
  1892. wrtndate proc    near
  1893.     mov    ah,getdate        ; DOS date (cx= yyyy, dh= mm, dl= dd)
  1894.     int    dos
  1895.     push    dx            ; save dx
  1896.     mov    ax,cx            ; year
  1897.     call    fdec2di            ; convert it
  1898.     pop    dx            ; get mm:dd
  1899.     push    dx
  1900.     mov    al,dh            ; months are next
  1901.     xor    ah,ah
  1902.     cmp    al,10            ; less than 10?
  1903.     ja    wrtndat1        ; a = no
  1904.     mov    byte ptr [di],'0'    ; leading 0
  1905.     inc    di
  1906. wrtndat1:call    fdec2di
  1907.     pop    dx
  1908.     mov    al,dl            ; get days
  1909.     xor    ah,ah
  1910.     cmp    al,10            ; less than 10?
  1911.     ja    wrtndat2        ; a = no
  1912.     mov    byte ptr [di],'0'    ; leading 0
  1913.     inc    di
  1914. wrtndat2:call    fdec2di
  1915.     mov    word ptr [di],0020h    ; space and null terminator
  1916.     inc    di
  1917.     ret
  1918. wrtndate endp
  1919.  
  1920. ; write \v(DIRECTORY) text to ds:di
  1921. wrtdir    proc    near
  1922.     push    si
  1923.     mov    ah,gcurdsk        ; get current disk
  1924.     int    dos
  1925.     add    al,'A'            ; make al = 0 == 'A'
  1926.     mov    [di],al
  1927.     mov    word ptr [di+1],'\:'
  1928.     mov    si,di            ; work buffer
  1929.     add    si,3            ; end with a colon and backslash
  1930.     mov    ah,gcd            ; get current directory
  1931.     xor    dl,dl            ; use current drive
  1932.     int    dos            ; get ds:si = asciiz path (no drive)
  1933.     mov    dx,di
  1934.     call    fstrlen
  1935.     add    di,cx
  1936.     mov    word ptr [di],0020h    ; space and null terminator
  1937.     inc    di
  1938.     pop    si
  1939.     ret
  1940. wrtdir    endp
  1941.  
  1942. ; write \v(PLATFORM) text to ds:di
  1943. wrtplat    proc    near
  1944.     push    si
  1945.     mov    si,offset machnam    ; machine name in sys dep file
  1946.     cld
  1947. wrtplat1:lodsb                ; get a char
  1948.     cmp    al,'$'            ; terminator?
  1949.     je    wrtplat2        ; e = yes
  1950.     mov    [di],al            ; store char
  1951.     inc    di
  1952.     jmp    short wrtplat1        ; keep going
  1953. wrtplat2:mov    word ptr [di],0020h    ; space and null terminator
  1954.     inc    di
  1955.     mov    temp,di            ; place for additional text
  1956.     pop    si
  1957.     ret
  1958. wrtplat    endp
  1959.  
  1960. wrtport    proc    near
  1961.     push    bx
  1962.     push    si
  1963.     mov    al,flags.comflg        ; get coms port indicator
  1964.     mov    bx,offset comptab    ; table of comms ports
  1965.     mov    cl,[bx]            ; number of entries
  1966.     xor    ch,ch
  1967.     inc    bx
  1968. wrtpor3:mov    dx,[bx]            ; length of this entry
  1969.     mov    si,bx
  1970.     add    si,2            ; points to entry text string
  1971.     add    si,dx            ; point to qualifier
  1972.     cmp    [si],al            ; our port?
  1973.     je    wrtpor4            ; e = yes
  1974.     add    bx,[bx]            ; add text length
  1975.     add    bx,4            ; plus count and qualifier
  1976.     loop    wrtpor3            ; next entry
  1977.     jmp    short wrtpor5        ; no match, curious
  1978. wrtpor4:mov    si,bx            ; point at entry
  1979.     add    si,2            ; point at string
  1980.     mov    cx,[bx]            ; length of string
  1981.     push    es
  1982.     mov    ax,ds
  1983.     mov    es,ax
  1984.     cld
  1985.     rep    movsb            ; copy to DS:DI
  1986.     pop    es
  1987. wrtpor5:mov    word ptr [di],0020h    ; space and null terminator
  1988.     inc    di
  1989.     pop    si
  1990.     pop    bx
  1991.     ret
  1992. wrtport    endp
  1993.  
  1994. ; write \v(PROGRAM) text to ds:si
  1995. wrtprog    proc    near
  1996.     push    si
  1997.     mov    si,offset progm        ; source string
  1998.     cld
  1999. wrtprg1:lodsb
  2000.     cmp    al,'$'            ; terminator?
  2001.     je    wrtprg2            ; e = yes
  2002.     mov    [di],al            ; store the char
  2003.     inc    di
  2004.     jmp    short wrtprg1
  2005. wrtprg2:mov    word ptr [di],0020h    ; space and null terminator
  2006.     inc    di
  2007.     mov    temp,di            ; place for additional text
  2008.     pop    si
  2009.     ret
  2010. wrtprog    endp
  2011.  
  2012. ; write \v(STATUS) text to ds:di
  2013. wrtstat    proc    near
  2014.     mov    ax,kstatus        ; Kermit status word
  2015.     call    fdec2di
  2016.     mov    word ptr [di],0020h    ; space and null terminator
  2017.     inc    di
  2018.     mov    temp,di            ; place for additional text
  2019.     ret
  2020. wrtstat    endp
  2021.  
  2022. ; write \v(SYSTEM) text to ds:di
  2023. wrtsystem proc    near
  2024.     push    si
  2025.     mov    si,offset system    ; system string "MS-DOS", dollar sign
  2026.     cld
  2027.     jmp    wrtplat1        ; use some common code
  2028. wrtsystem endp
  2029.  
  2030. ; write \v(TIME) text to ds:di
  2031. wrttime    proc    near
  2032.     mov    ah,gettim        ; DOS tod (ch=hh, cl=mm, dh=ss, dl=.s)
  2033.     int    dos
  2034.     push    dx            ; save dx
  2035.     xor    ah,ah
  2036.     mov    al,ch            ; Hours
  2037.     cmp    al,10            ; leading digit?
  2038.     jae    wrttim1            ; ae = yes
  2039.     mov    byte ptr [di],'0'    ; make our own
  2040.     inc    di
  2041. wrttim1:push    cx
  2042.     call    fdec2di            ; write decimal asciiz to buffer
  2043.     pop    cx
  2044.     mov    byte ptr [di],':'
  2045.     inc    di
  2046.     xor    ah,ah
  2047.     mov    al,cl            ; Minutes
  2048.     cmp    al,10            ; leading digit?
  2049.     jae    wrttim2            ; ae = yes
  2050.     mov    byte ptr [di],'0'    ; make our own
  2051.     inc    di
  2052. wrttim2:call    fdec2di            ; write decimal asciiz to buffer
  2053.     mov    byte ptr [di],':'
  2054.     inc    di
  2055.     pop    dx
  2056.     xor    ah,ah
  2057.     mov    al,dh            ; Seconds
  2058.     cmp    al,10            ; leading digit?
  2059.     jae    wrttim3            ; ae = yes
  2060.     mov    byte ptr [di],'0'    ; make our own
  2061.     inc    di
  2062. wrttim3:call    fdec2di            ; write decimal asciiz to buffer
  2063.     mov    word ptr [di],0020h    ; space and null terminator
  2064.     inc    di
  2065.     mov    temp,di            ; place for additional text
  2066.     ret
  2067. wrttime    endp
  2068.  
  2069. ; write \v(Version) text to ds:di
  2070. wrtver    proc    near
  2071.     mov    si,offset verident    ; MS Kermit version string in mssker
  2072.     cld
  2073. wrtver1:lodsb
  2074.     mov    [di],al
  2075.     inc    di
  2076.     cmp    al,'$'            ; end of string?
  2077.     jne    wrtver1            ; ne = no, continue copying
  2078.     dec    di
  2079.     mov    word ptr [di],0020h    ; space and null terminator
  2080.     inc    di
  2081.     mov    temp,di            ; place for additional text
  2082.     ret
  2083. wrtver    endp
  2084.  
  2085. code1    ends
  2086.  
  2087. code    segment
  2088.     assume    cs:code
  2089.  
  2090. ; Set master prompt level. Enter with DX = offset of prompt string
  2091. MPROMPT    proc    near
  2092.     mov    mcmprmp,dx        ; offset of prompt string
  2093.     pop    ax            ; get the return address
  2094.     mov    word ptr mcmrprs,ax     ; offset to go to on reparse
  2095.     mov    mcmostp,sp        ; stack pointer at reparse time
  2096.     push    ax            ; put it on the stack again
  2097.     mov    ax,cs            ; our current code segment
  2098.     mov    word ptr mcmrprs+2,ax     ; segment of reparse address
  2099.     jmp    short prompt        ; now set the active prompt material
  2100. MPROMPT    endp
  2101.  
  2102.     
  2103. ; This routine prints the prompt and specifies the reparse address.
  2104. ; Enter with pointer to prompt string in dx. 
  2105. PROMPT    PROC  NEAR
  2106.     mov    comand.cmprmp,dx    ; save the prompt
  2107.     pop    ax            ; get the return address
  2108.     mov    word ptr comand.cmrprs,ax ; offset to go to on reparse
  2109.     mov    comand.cmostp,sp    ; save for later restoration
  2110.     push    ax            ; put it on the stack again
  2111.     mov    ax,cs            ; our current code segment
  2112.     mov    word ptr comand.cmrprs+2,ax ; segment of reparse address
  2113.     mov    ax,offset cmdbuf
  2114.     mov    cmwptr,ax        ; reset buffer read/write pointers
  2115.     mov    cmrptr,ax
  2116.     xor    al,al
  2117.     mov    comand.cmper,al        ; allow substitutions
  2118.     mov    cmsflg,0ffh        ; remove leading spaces
  2119.     cmp    flags.takflg,al        ; look at Take flag, zero?
  2120.     jne    promp1            ; ne=supposed to echo, skip this check
  2121.     cmp    taklev,al        ; inside a take file?
  2122.     je    promp1            ; no, keep going
  2123.     ret                ; yes, return
  2124. promp1:    mov    ah,prstr
  2125.     mov    dx,offset crlf
  2126.     int    dos
  2127.     mov    dx,comand.cmprmp    ; prompt pointer
  2128.     call    prtasz            ; show asciiz prompt string
  2129.     clc
  2130.     ret
  2131. PROMPT    ENDP
  2132.  
  2133. ISDEV    PROC    NEAR            ; Set carry if STDIN is non-disk
  2134.     push    ax
  2135.     push    bx
  2136.     push    dx
  2137.     xor    bx,bx            ; handle 0 is stdin
  2138.     xor    al,al            ; get device info
  2139.     mov    ah,ioctl
  2140.     int    dos
  2141.     rcl    dl,1            ; put ISDEV bit into the carry bit
  2142.     pop    dx            ; carry is set if device
  2143.     pop    bx
  2144.     pop    ax
  2145.     ret                ; carry set if device
  2146. ISDEV    ENDP
  2147.  
  2148. ISEOF    PROC    NEAR            ; Set carry if STDIN is at EOF
  2149.     push    ax            ;  but only if stdin is a non-device
  2150.     push    bx
  2151.     push    dx
  2152.     xor    bx,bx            ; handle 0 is stdin
  2153.     xor    al,al            ; get device info
  2154.     mov    ah,ioctl
  2155.     int    dos
  2156.     mov    ah,ioctl
  2157.     mov    al,6            ; get handle input status, set al
  2158.     test    dl,80h            ; bit set if handle is for a device
  2159.     jnz    iseof1            ; nz = device, always ready (al != 0)
  2160.     int    dos
  2161. iseof1:    or    al,al            ; EOF?
  2162.     pop    dx
  2163.     pop    bx
  2164.     pop    ax
  2165.     jnz    iseof2            ; nz = no
  2166.     stc                ; set carry for eof
  2167.     ret
  2168. iseof2:    clc                ; clear carry for not-eof
  2169.     ret
  2170. ISEOF    ENDP
  2171.  
  2172. ; Convert ascii characters in al and ah to lowercase.
  2173. ; All registers are preserved except AX, of course.
  2174.  
  2175. TOLOWR PROC NEAR
  2176.     cmp    ah,'A'            ; less that cap A?
  2177.     jl    tolow1            ; l = yes. leave untouched
  2178.     cmp    ah,'Z'+1        ; more than cap Z?
  2179.     jns    tolow1            ; ns = yes
  2180.     or    ah,20H            ; convert to lowercase
  2181. tolow1:    cmp    al,'A'            ; less that cap A?
  2182.     jl    tolow2            ; l = yes. leave untouched
  2183.     cmp    al,'Z'+1        ; more than cap Z?
  2184.     jns    tolow2            ; ns = yes
  2185.     or    al,20H            ; convert to lowercase
  2186. tolow2:    ret
  2187. TOLOWR    endp
  2188.  
  2189. ; Parse control sequences and device control strings.
  2190. ; Expect CSI, Escape [, or DCS lead-in characters to have been read.
  2191. ; Puts numerical Parameters in array param (16 bits, count is nparam) and
  2192. ;  a single letter Parameter in lparam, (Parameters are all ASCII column 3)
  2193. ;  Intermediate characters in array inter (count is ninter), (ASCII column 2)
  2194. ;  Final character in AL (ASCII columns 4-7).
  2195. ; Invoke by setting state to offset atparse, set pardone to offset of
  2196. ; procedure to jump to after reading Final char (0 means do just ret)
  2197. ; and optionally setting parfail to address to jump to if parsing failure.
  2198. ; When the Final char has been accepted this routine jumps to label held in
  2199. ; pardone for final action. Before the Final char has been read successful
  2200. ; operations return carry clear.
  2201. ; Failure exits are carry set, and an optional jump through parfail (if 
  2202. ; non-zero) or a return.
  2203.  
  2204. atparse    proc    near
  2205.     mov    bx,parstate        ; get parsing state
  2206.     or    bx,bx            ; have any state?
  2207.     jnz    atpars1            ; nz = have a state
  2208.     call    atpclr            ; do initialization
  2209.     mov    bx,parstate        ; get initial state
  2210. atpars1:call    bx            ; execute it
  2211.     jc    atpfail            ; c = failure
  2212.     cmp    parstate,offset atpdone    ; parsed final char?
  2213.     je    atpdone            ; e = yes
  2214.     ret                ; no, wait for another char
  2215.  
  2216.                 ; successful conclusion, final char is in AL
  2217. atpdone:mov    parstate,0        ; reset parsing state
  2218.     cmp    pardone,0        ; separate return address defined?
  2219.     jne    atpdon1            ; ne = yes
  2220.     clc
  2221.     ret                ; else just return
  2222. atpdon1:clc
  2223.     jmp    pardone            ; jmp to supplied action routine
  2224.  
  2225. atpfail:mov    parstate,0        ; failed, reset parser to normal state
  2226.     cmp    parfail,0        ; jump address specified?
  2227.     je    atpfail1        ; e = no
  2228.     jmp    parfail            ; yes, exit this way
  2229. atpfail1:stc
  2230.     ret
  2231.                     ; parsing workers
  2232. atparm:    cmp    ninter,0        ; Parameter, started intermediate yet?
  2233.     jne    atinter            ; ne = yes, no more parameters
  2234.     cmp    al,';'            ; argument separator?
  2235.     jne    atparm3            ; ne = no
  2236.     mov    ax,nparam        ; number of Parameters
  2237.     inc    ax            ; say a new one
  2238.     cmp    ax,maxparam        ; too many?
  2239.     jb    atparm2            ; b = no, continue
  2240.     stc                ; set carry to say failed
  2241.     ret                ; too many, ignore remainder
  2242. atparm2:mov    nparam,ax        ; say doing another Parameter
  2243.     clc
  2244.     ret
  2245.  
  2246. atparm3:mov    ah,al            ; copy char
  2247.     and    ah,not 0fh        ; ignore low nibble
  2248.     cmp    ah,30h            ; column 3, row 0? (30h='0')
  2249.     jne    atparm6            ; ne = no, check Intermediate/Final
  2250.     cmp    al,'9'            ; digit?
  2251.     ja    atparm5            ; a = no, check letter Parameters
  2252.     sub    al,'0'            ; ascii to binary
  2253.     mov    bx,nparam        ; current parameter number
  2254.     shl    bx,1            ; convert to word index
  2255.     mov    cx,param[bx]        ; current parameter value
  2256.     shl    cx,1            ; multiply by 10.  2 * cl
  2257.     push    bx
  2258.     mov    bx,cx            ; save 2 * cl
  2259.     shl    cx,1            ; 4 * cl
  2260.     shl    cx,1            ; 8 * cl
  2261.     add    cx,bx            ; 10 * cl
  2262.     pop    bx
  2263.     add    cl,al            ; add new digit
  2264.     adc    ch,0
  2265.     jnc    atparm4            ; nc = no carry out (65K or below)
  2266.     mov    cx,0ffffh        ; set to max value
  2267. atparm4:mov    param[bx],cx        ; current Parameter value
  2268.     clc
  2269.     ret
  2270.                     ; check non-numeric Parameters
  2271. atparm5:cmp    al,'?'            ; within column 3?
  2272.     ja    atfinal            ; a = no, check Final char
  2273.     mov    lparam,al        ; store non-numeric Parameter
  2274.     clc
  2275.     ret
  2276.  
  2277. atparm6:cmp    nparam,0        ; started a parameter yet?
  2278.     jne    atparm7            ; ne = yes
  2279.     cmp    param,0            ; got anything for param[0]?
  2280.     je    atinter            ; e = no
  2281. atparm7:inc    nparam            ; yes, say finished with another
  2282.  
  2283. atinter:mov    parstate,offset atinter    ; next state (intermediate)
  2284.     cmp    al,';'            ; argument separator?
  2285.     jne    atinte1            ; ne = no
  2286.     mov    ax,ninter        ; number of Intermediates 
  2287.     cmp    ax,maxinter        ; too many?
  2288.     jb    atinte2            ; b = no, continue
  2289.     stc                ; carry = failed
  2290.     ret                ; too many, ignore remainder
  2291. atinte1:test    al,not 2fh        ; column two = 20h - 2fh?
  2292.     jnz    atfinal            ; nz = not an Intermediate, try Final
  2293.     mov    bx,ninter        ; current Intermediate slot number
  2294.     mov    inter[bx],al        ; current Intermediate value
  2295. atinte2:inc    ninter            ; say doing another Intermediate
  2296.     clc
  2297.     ret
  2298.  
  2299. atfinal:cmp    al,40h            ; Final character, range is 40h to 7fh
  2300.     jb    atfina1            ; b = out of range
  2301.     cmp    al,7fh
  2302.     ja    atfina1            ; a = out of range
  2303.     mov    parstate,offset atpdone    ; next state is "done"
  2304.     clc                ; success, final char is in AL
  2305.     ret
  2306. atfina1:stc                ; c = failed
  2307.     ret
  2308. atparse    endp
  2309.  
  2310. ; Clear Parameter, Intermediate arrays in preparation for parsing
  2311. atpclr    proc    near
  2312.     push    ax
  2313.     push    cx
  2314.     push    di
  2315.     push    es
  2316.     xor    ax,ax            ; get a null
  2317.     mov    parstate,offset atparm    ; init parser state
  2318.     mov    lparam,al        ; clear letter Parameter
  2319.     mov    nparam,ax        ; clear Parameter count
  2320.     mov    cx,maxparam        ; number of Parameter slots
  2321.     mov    di,offset param        ; Parameter slots
  2322.     push    ds
  2323.     pop    es            ; use data segment for es:di below
  2324.     cld                ; set direction forward
  2325.     rep    stosw            ; clear the slots
  2326.     mov    ninter,ax        ; clear Intermediate count
  2327.     mov    cx,maxinter        ; number of Intermediate slots
  2328.     mov    di,offset inter        ; Intermediate slots
  2329.     rep    stosb            ; clear the slots
  2330.     pop    es
  2331.     pop    di
  2332.     pop    cx
  2333.     pop    ax
  2334.     ret
  2335. atpclr    endp
  2336.  
  2337. ; Dispatch table processor. Enter with BX pointing at table of {char count,
  2338. ; address of action routines, characters}. Jump to matching routine or return.
  2339. ; Enter with AL holding received Final char.
  2340. atdispat proc near
  2341.     mov    cl,[bx]            ; get table length from first byte
  2342.     xor    ch,ch
  2343.     mov    di,bx            ; main table
  2344.     add    di,3            ; point di at first char in table
  2345.     push    es
  2346.     push    ds
  2347.     pop    es            ; use data segment for es:di below
  2348.     cld                ; set direction forward
  2349.     repne    scasb            ; find matching character
  2350.     pop    es
  2351.     je    atdisp2            ; e = found a match, get action addr
  2352.     ret                ; ignore escape sequence
  2353. atdisp2:sub    di,bx            ; distance scanned in table
  2354.     sub    di,4            ; skip count byte, address word, inc
  2355.     shl    di,1            ; convert to word index
  2356.     inc    bx            ; point to address of action routines
  2357.     mov    bx,[bx]            ; get address of action table
  2358.     jmp    word ptr [bx+di]    ; dispatch to the routine
  2359. atdispat endp
  2360. code    ends
  2361.     end
  2362.  
  2363.